How to do it...

Execute these steps one by one:

  1. In the likes app, create a templatetags directory with an empty __init__.py file to make it a Python module. Then, add the likes_tags.py file, where we'll define the {% like_widget %} template tag as follows:
# likes/templatetags/likes_tags.py
from django import template
from django.contrib.contenttypes.models import ContentType
from django.template.loader import render_to_string

from likes.models import Like

register = template.Library()


class ObjectLikeWidget(template.Node):
def __init__(self, var):
self.var = var

def render(self, context):
liked_object = self.var.resolve(context)
ct = ContentType.objects.get_for_model(liked_object)
user = context["request"].user

if not user.is_authenticated:
return ""

context.push(object=liked_object,
content_type_id=ct.pk)
# is_liked_by_user=liked_by(liked_object,
# user),
# count=liked_count(liked_object))
output = render_to_string("likes/includes/widget.html",
context.flatten())
context.pop()
return output


# TAGS

@register.tag
def like_widget(parser, token):
try:
tag_name, for_str, var_name = token.split_contents()
except ValueError:
tag_name = "%r" % token.contents.split()[0]
raise template.TemplateSyntaxError(
f"{tag_name} tag requires a following syntax: "
f"{{% {tag_name} for <object> %}}")
var = template.Variable(var_name)
return ObjectLikeWidget(var)
  1. Also, we'll add filters in the same file to get the like status for a user and the total number of likes for a specified object:
# likes/templatetags/likes/likes_tags.py
# ...

# FILTERS

@register.filter
def liked_by(obj, user):
ct = ContentType.objects.get_for_model(obj)
liked = Like.objects.filter(user=user,
content_type=ct,
object_id=obj.pk)
return liked.count() > 0


@register.filter
def liked_count(obj):
ct = ContentType.objects.get_for_model(obj)
likes = Like.objects.filter(content_type=ct,
object_id=obj.pk)
return likes.count()
  1. In the URL rules, we need a rule for a view, which will handle the liking and unliking using Ajax:
# likes/urls.py
from django.urls import path

from .views import json_set_like


urlpatterns = [
path("<int:content_type_id>/<int:object_id>/",
json_set_like,
name="json-set-like")
]
  1. Make sure to map the URLs to the project as well:
# project/urls.py
from django.urls import include, path
# ...

urlpatterns = [
# ...
path('like/', include('likes.urls')),
]
  1. Then, we need to define the view, as shown in the following code:
# likes/views.py
import json

from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponse
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt

from .models import Like
from .templatetags.likes_tags import liked_count


@never_cache
@csrf_exempt
def json_set_like(request, content_type_id, object_id):
"""
Sets the object as a favorite for the current user
"""
result = {
"success": False,
}
if request.user.is_authenticated and request.method == "POST":
content_type = ContentType.objects.get(id=content_type_id)
obj = content_type.get_object_for_this_type(pk=object_id)

like, is_created = Like.objects.get_or_create(
content_type=ContentType.objects.get_for_model(obj),
object_id=obj.pk,
user=request.user)
if not is_created:
like.delete()

result = {
"success": True,
"action": "add" if is_created else "remove",
"count": liked_count(obj),
}

json_str = json.dumps(result, ensure_ascii=False)
return HttpResponse(json_str, content_type="application/json")

  1. In the template for the list or detail view of any object, we can add the template tag for the widget. Let's add the widget to the location detail that we created in the previous recipes, as follows:
{# templates/locations/location_detail.html #}
{% extends "base.html" %}
{% load likes_tags static thumbnail %}

{% block content %}
<h2 class="map-title">{{ location.title }}</h2>
{% if request.user.is_authenticated %}
{% like_widget for location %}
{% endif %} {# ... #} {% endblock %} {% block js %}
<script src="{% static 'likes/js/widget.js' %}"></script>
{# ... #} {% endblock %}
  1. Then, we need a template for the widget, as shown in the following code:
{# templates/likes/includes/widget.html #}
{% load i18n %}
<p class="like-widget">
<button type="button"
class="like-button btn btn-primary
{% if is_liked_by_user %} active{% endif %}"
data-href="{% url "json_set_like"
content_type_id=content_type_id
object_id=object.pk %}"
data-remove-label="{% trans "Like" %}"
data-add-label="{% trans "Unlike" %}">
{% if is_liked_by_user %}
<span class="glyphicon glyphicon-star"></span>
{% trans "Unlike" %}
{% else %}
<span class="glyphicon glyphicon-star-empty"></span>
{% trans "Like" %}
{% endif %}
</button>
<span class="like-badge badge badge-secondary">
{{ count }}</span>
</p>
The template tags in the preceding snippet have been split across lines for legibility, but in practice, template tags must be on a single line, and so they cannot be split in this manner.
  1. Finally, we create JavaScript to handle the liking and unliking action in the browser, as follows:
// static/likes/js/widget.js
(function($) {
var star = {
add: '<span class="glyphicon glyphicon-star"></span>',
remove: '<span class="glyphicon glyphicon-star-empty"></span>'
};

$(document).on("click", ".like-button", function() {
var $button = $(this);
var $widget = $button.closest(".like-widget");
var $badge = $widget.find(".like-badge");

$.post($button.data("href"), function(data) {
if (data.success) {
var action = data.action; // "add" or "remove"
var label = $button.data(action + "-label");

$button[action + "Class"]("active");
$button.html(star[action] + " " + label);

$badge.html(data.count);
}
}, "json");
});
}(jQuery));
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.174.95