From 39789d7225dc968e8deaae4028bbe85ecce4c6fa Mon Sep 17 00:00:00 2001 From: Cyrill Popov Date: Tue, 19 Dec 2023 09:42:22 +0300 Subject: [PATCH] Add likes for images --- djangocms_picture/admin.py | 31 ++++++++++++++ djangocms_picture/cms_plugins.py | 1 + djangocms_picture/migrations/0018_likes.py | 38 +++++++++++++++++ djangocms_picture/models.py | 21 +++++++++- .../djangocms_picture/admin/likes.html | 25 +++++++++++ djangocms_picture/templatetags/__init__.py | 0 .../templatetags/djangocms_picture_tags.py | 24 +++++++++++ djangocms_picture/urls.py | 7 ++++ djangocms_picture/views.py | 42 +++++++++++++++++++ 9 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 djangocms_picture/admin.py create mode 100644 djangocms_picture/migrations/0018_likes.py create mode 100644 djangocms_picture/templates/djangocms_picture/admin/likes.html create mode 100755 djangocms_picture/templatetags/__init__.py create mode 100755 djangocms_picture/templatetags/djangocms_picture_tags.py create mode 100644 djangocms_picture/urls.py create mode 100644 djangocms_picture/views.py diff --git a/djangocms_picture/admin.py b/djangocms_picture/admin.py new file mode 100644 index 0000000..178e898 --- /dev/null +++ b/djangocms_picture/admin.py @@ -0,0 +1,31 @@ +from django.contrib import admin +from django.db.models import Count +from django.template.response import TemplateResponse +from django.urls import path + +from filer.models import Image + +from .models import ImageLike + + +class ImageLikeAdmin(admin.ModelAdmin): + def get_urls(self): + urls = super().get_urls() + prefix = f'{self.model._meta.app_label}_{self.model._meta.model_name}' + extra_urls = [path('likes/', self.admin_site.admin_view(self.likes), name=f'{prefix}_likes')] + return extra_urls + urls + + def likes(self, request): + context = self.admin_site.each_context(request) + likes = { + like['image_id']: like['cnt'] + for like in ImageLike.objects.values('image_id').annotate(cnt=Count('image_id')).order_by('-cnt') + } + images = Image.objects.filter(id__in=likes.keys()) + for image in images: + image.likes_count = likes[image.id] + context['images'] = sorted(images, key=lambda x: x.likes_count, reverse=True) + return TemplateResponse(request, 'djangocms_picture/admin/likes.html', context) + + +admin.site.register(ImageLike, ImageLikeAdmin) diff --git a/djangocms_picture/cms_plugins.py b/djangocms_picture/cms_plugins.py index b21bc70..0ee0795 100644 --- a/djangocms_picture/cms_plugins.py +++ b/djangocms_picture/cms_plugins.py @@ -31,6 +31,7 @@ class PicturePlugin(CMSPluginBase): 'dark_inverse', 'external_picture', 'is_lightbox', + 'use_likes', ) }), ('Подпись', { diff --git a/djangocms_picture/migrations/0018_likes.py b/djangocms_picture/migrations/0018_likes.py new file mode 100644 index 0000000..a2c91bc --- /dev/null +++ b/djangocms_picture/migrations/0018_likes.py @@ -0,0 +1,38 @@ +# Generated by Django 3.2.23 on 2023-12-18 12:30 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.FILER_IMAGE_MODEL), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('djangocms_picture', '0017_title'), + ] + + operations = [ + migrations.AddField( + model_name='picture', + name='use_likes', + field=models.BooleanField(default=False, verbose_name='включить "лайки"'), + ), + migrations.CreateModel( + name='ImageLike', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='дата')), + ('image', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.FILER_IMAGE_MODEL)), + ('user', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'лайки изображений', + 'verbose_name_plural': 'лайк изображения', + 'ordering': ('-created_at',), + }, + ), + ] diff --git a/djangocms_picture/models.py b/djangocms_picture/models.py index bb4135f..1b90bac 100644 --- a/djangocms_picture/models.py +++ b/djangocms_picture/models.py @@ -2,6 +2,8 @@ Enables the user to add an "Image" plugin that displays an image using the HTML tag. """ +import uuid + from cms.models import CMSPlugin from cms.models.fields import PageField from django.conf import settings @@ -13,7 +15,7 @@ from djangocms_attributes_field.fields import AttributesField from easy_thumbnails.files import get_thumbnailer from filer.fields.image import FilerImageField -from filer.models import ThumbnailOption +from filer.models import Image, ThumbnailOption # add setting for picture alignment, renders a class or inline styles @@ -233,6 +235,8 @@ class AbstractPicture(CMSPlugin): title = models.CharField('Заголовок', max_length=255, blank=True) text = models.TextField('Тело подписи', max_length=2048, blank=True) + use_likes = models.BooleanField('включить "лайки"', default=False) + class Meta: abstract = True @@ -474,3 +478,18 @@ class Picture(AbstractPicture): class Meta: abstract = False + + +class ImageLike(models.Model): + image = models.ForeignKey(Image, on_delete=models.CASCADE) + user = models.ForeignKey(settings.AUTH_USER_MODEL, editable=False, blank=True, null=True, on_delete=models.SET_NULL) + uuid = models.UUIDField(default=uuid.uuid4, editable=False) + created_at = models.DateTimeField('дата', auto_now_add=True) + + class Meta: + verbose_name_plural = 'лайк изображения' + verbose_name = _('лайки изображений') + ordering = ('-created_at',) + + def __str__(self): + return f'{self.image}' diff --git a/djangocms_picture/templates/djangocms_picture/admin/likes.html b/djangocms_picture/templates/djangocms_picture/admin/likes.html new file mode 100644 index 0000000..3eb85e6 --- /dev/null +++ b/djangocms_picture/templates/djangocms_picture/admin/likes.html @@ -0,0 +1,25 @@ +{% extends 'admin/base_site.html' %} + + +{% load i18n %} + + +{% block breadcrumbs %} + +{% endblock %} + + +{% block content %} + + {% for image in images %} + + + + + {% endfor %} +

{{ image.likes_count }}

+{% endblock %} diff --git a/djangocms_picture/templatetags/__init__.py b/djangocms_picture/templatetags/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/djangocms_picture/templatetags/djangocms_picture_tags.py b/djangocms_picture/templatetags/djangocms_picture_tags.py new file mode 100755 index 0000000..abd0289 --- /dev/null +++ b/djangocms_picture/templatetags/djangocms_picture_tags.py @@ -0,0 +1,24 @@ +import uuid + +from django import template + +from ..models import ImageLike + +register = template.Library() + + +@register.simple_tag() +def image_has_like(request, image): + cookie_name = 'djangocms_picture_like' + flt = {} + + if request.user.is_authenticated: + flt['user'] = request.user + else: + flt['uuid'] = request.COOKIES.get(cookie_name, uuid.uuid4()) + + try: + ImageLike.objects.get(image=image, **flt) + return True + except ImageLike.DoesNotExist: + return False diff --git a/djangocms_picture/urls.py b/djangocms_picture/urls.py new file mode 100644 index 0000000..e52c132 --- /dev/null +++ b/djangocms_picture/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from .views import LikeView + +urlpatterns = [ + path('like//', LikeView.as_view(), name='djangocmspicture-like'), +] diff --git a/djangocms_picture/views.py b/djangocms_picture/views.py new file mode 100644 index 0000000..ce8879c --- /dev/null +++ b/djangocms_picture/views.py @@ -0,0 +1,42 @@ +import uuid + +from filer.models import Image + +from django.http import JsonResponse +from django.views.decorators.csrf import csrf_exempt +from django.views.generic.detail import BaseDetailView + +from .models import ImageLike + + +class LikeView(BaseDetailView): + model = Image + + @csrf_exempt + def dispatch(self, request, *args, **kwargs): + return super().dispatch(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + image = self.get_object() + flt = {} + cookie_name = 'djangocms_picture_like' + + if request.user.is_authenticated: + flt['user'] = request.user + else: + flt['uuid'] = request.COOKIES.get(cookie_name, uuid.uuid4()) + + try: + like = ImageLike.objects.get(image=image, **flt) + like.delete() + result = 'deleted' + except ImageLike.DoesNotExist: + like = ImageLike.objects.create(image=image, **flt) + result = 'created' + + response = JsonResponse({'result': result, 'like_id': like.id}) + + if 'uuid' in flt: + response.set_cookie(cookie_name, flt['uuid']) + + return response