From 605c81b33c989bf346248bcd8fa456a185644f7b Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 4 Sep 2019 18:47:06 +0300 Subject: [PATCH] replace comment model from establishment app, added new app comment --- apps/comment/__init__.py | 0 apps/comment/admin.py | 8 +++ apps/comment/apps.py | 8 +++ apps/comment/migrations/0001_initial.py | 36 +++++++++++ apps/comment/migrations/__init__.py | 0 apps/comment/models.py | 51 ++++++++++++++++ apps/comment/serializers/__init__.py | 0 apps/comment/serializers/common.py | 60 +++++++++++++++++++ apps/comment/serializers/mobile.py | 0 apps/comment/serializers/web.py | 1 + apps/comment/tests.py | 1 + apps/comment/urls/__init__.py | 0 apps/comment/urls/common.py | 12 ++++ apps/comment/urls/mobile.py | 9 +++ apps/comment/urls/web.py | 9 +++ apps/comment/views/__init__.py | 0 apps/comment/views/common.py | 34 +++++++++++ apps/comment/views/mobile.py | 0 apps/comment/views/web.py | 0 apps/establishment/admin.py | 16 ++--- .../migrations/0015_delete_comment.py | 16 +++++ apps/establishment/models.py | 38 +----------- apps/establishment/serializers.py | 30 ++++------ project/settings/base.py | 1 + project/urls/web.py | 1 + 25 files changed, 271 insertions(+), 60 deletions(-) create mode 100644 apps/comment/__init__.py create mode 100644 apps/comment/admin.py create mode 100644 apps/comment/apps.py create mode 100644 apps/comment/migrations/0001_initial.py create mode 100644 apps/comment/migrations/__init__.py create mode 100644 apps/comment/models.py create mode 100644 apps/comment/serializers/__init__.py create mode 100644 apps/comment/serializers/common.py create mode 100644 apps/comment/serializers/mobile.py create mode 100644 apps/comment/serializers/web.py create mode 100644 apps/comment/tests.py create mode 100644 apps/comment/urls/__init__.py create mode 100644 apps/comment/urls/common.py create mode 100644 apps/comment/urls/mobile.py create mode 100644 apps/comment/urls/web.py create mode 100644 apps/comment/views/__init__.py create mode 100644 apps/comment/views/common.py create mode 100644 apps/comment/views/mobile.py create mode 100644 apps/comment/views/web.py create mode 100644 apps/establishment/migrations/0015_delete_comment.py diff --git a/apps/comment/__init__.py b/apps/comment/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/comment/admin.py b/apps/comment/admin.py new file mode 100644 index 00000000..855f6b3e --- /dev/null +++ b/apps/comment/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin + +from . import models + + +@admin.register(models.Comment) +class CommentModelAdmin(admin.ModelAdmin): + """Model admin for model Comment""" diff --git a/apps/comment/apps.py b/apps/comment/apps.py new file mode 100644 index 00000000..d19caa6c --- /dev/null +++ b/apps/comment/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class CommentConfig(AppConfig): + name = 'comment' + verbose_name = _('comment') + verbose_name_plural = _('comments') diff --git a/apps/comment/migrations/0001_initial.py b/apps/comment/migrations/0001_initial.py new file mode 100644 index 00000000..9691d945 --- /dev/null +++ b/apps/comment/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# Generated by Django 2.2.4 on 2019-09-04 14:03 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('text', models.TextField(verbose_name='Comment text')), + ('mark', models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='Mark')), + ('object_id', models.PositiveIntegerField()), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'verbose_name': 'Comment', + 'verbose_name_plural': 'Comments', + }, + ), + ] diff --git a/apps/comment/migrations/__init__.py b/apps/comment/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/comment/models.py b/apps/comment/models.py new file mode 100644 index 00000000..ca6d39ec --- /dev/null +++ b/apps/comment/models.py @@ -0,0 +1,51 @@ +"""Models for app comment.""" +from django.contrib.contenttypes import fields as generic +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from account.models import User +from utils.models import ProjectBaseMixin + + +class CommentQuerySet(models.QuerySet): + """QuerySets for Comment model.""" + + def by_user(self, user: User): + """Return comments by author""" + return self.filter(user=user) + + def annotate_is_mine_status(self, user): + """Annotate belonging status""" + return self.annotate(is_mine=models.Case( + models.When( + models.Q(user=user), + then=True + ), + default=False, + output_field=models.BooleanField(default=False) + )) + + +class Comment(ProjectBaseMixin): + """Comment model.""" + text = models.TextField(verbose_name=_('Comment text')) + mark = models.PositiveIntegerField(blank=True, null=True, default=None, + verbose_name=_('Mark')) + user = models.ForeignKey('account.User', + related_name='comments', + on_delete=models.CASCADE, + verbose_name=_('User')) + content_type = models.ForeignKey(generic.ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + + objects = CommentQuerySet.as_manager() + + class Meta: + """Meta class""" + verbose_name = _('Comment') + verbose_name_plural = _('Comments') + + def __str__(self): + """String representation""" + return str(self.user) diff --git a/apps/comment/serializers/__init__.py b/apps/comment/serializers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/comment/serializers/common.py b/apps/comment/serializers/common.py new file mode 100644 index 00000000..8d11995f --- /dev/null +++ b/apps/comment/serializers/common.py @@ -0,0 +1,60 @@ +"""Common serializers for app comment.""" +from rest_framework import serializers + +from comment import models +from establishment.models import Establishment + + +class CommentBaseMixin(serializers.Serializer): + """Comment base serializer mixin""" + # RESPONSE + nickname = serializers.CharField(read_only=True, + source='user.username') + profile_pic = serializers.ImageField(read_only=True, + source='user.image') + + +class CommentSerializer(CommentBaseMixin, serializers.ModelSerializer): + """Comment serializer""" + is_mine = serializers.BooleanField(read_only=True) + + class Meta: + """Serializer for model Comment""" + model = models.Comment + fields = [ + 'id', + 'user_id', + 'is_mine', + 'created', + 'text', + 'mark', + 'nickname', + 'profile_pic' + ] + + +class EstablishmentCommentCreateSerializer(CommentSerializer): + """Create comment serializer""" + mark = serializers.IntegerField() + establishment_id = serializers.PrimaryKeyRelatedField(queryset=Establishment.objects.all(), + source='content_object', + write_only=True) + + class Meta: + """Serializer for model Comment""" + model = models.Comment + fields = [ + 'created', + 'text', + 'mark', + 'nickname', + 'profile_pic', + 'establishment_id', + ] + + def create(self, validated_data): + """Override create method""" + validated_data.update({ + 'user': self.context.get('request').user + }) + return super().create(validated_data) diff --git a/apps/comment/serializers/mobile.py b/apps/comment/serializers/mobile.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/comment/serializers/web.py b/apps/comment/serializers/web.py new file mode 100644 index 00000000..6db37a03 --- /dev/null +++ b/apps/comment/serializers/web.py @@ -0,0 +1 @@ +"""Serializers for app comment.""" diff --git a/apps/comment/tests.py b/apps/comment/tests.py new file mode 100644 index 00000000..a39b155a --- /dev/null +++ b/apps/comment/tests.py @@ -0,0 +1 @@ +# Create your tests here. diff --git a/apps/comment/urls/__init__.py b/apps/comment/urls/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/comment/urls/common.py b/apps/comment/urls/common.py new file mode 100644 index 00000000..7791f4e4 --- /dev/null +++ b/apps/comment/urls/common.py @@ -0,0 +1,12 @@ +"""Comment urlpaths.""" +from django.urls import path + +from comment.views import common as views + +app_name = 'comment' + +urlpatterns = [ + path('', views.CommentListView.as_view(), name='comment-list'), + path('create/', views.CommentCreateView.as_view(), name='comment-create'), + path('/', views.CommentRUD.as_view(), name='comment-rud'), +] diff --git a/apps/comment/urls/mobile.py b/apps/comment/urls/mobile.py new file mode 100644 index 00000000..8d58c5c7 --- /dev/null +++ b/apps/comment/urls/mobile.py @@ -0,0 +1,9 @@ +"""Mobile urlpaths.""" +from comment.urls.common import urlpatterns as common_urlpatterns + +app_name = 'comment' + +urlpatterns_api = [] + +urlpatterns = common_urlpatterns + \ + urlpatterns_api diff --git a/apps/comment/urls/web.py b/apps/comment/urls/web.py new file mode 100644 index 00000000..6141ceed --- /dev/null +++ b/apps/comment/urls/web.py @@ -0,0 +1,9 @@ +"""Web urlpaths.""" +from comment.urls.common import urlpatterns as common_urlpatterns + +app_name = 'comment' + +urlpatterns_api = [] + +urlpatterns = common_urlpatterns + \ + urlpatterns_api diff --git a/apps/comment/views/__init__.py b/apps/comment/views/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/comment/views/common.py b/apps/comment/views/common.py new file mode 100644 index 00000000..f492f4cb --- /dev/null +++ b/apps/comment/views/common.py @@ -0,0 +1,34 @@ +"""Views for app comment.""" +from rest_framework import generics +from rest_framework.permissions import AllowAny + +from comment.models import Comment +from comment.serializers import common as serializers + + +class CommentViewMixin: + """Mixin for Comment views""" + queryset = Comment.objects.order_by('-created') + serializer_class = serializers.CommentSerializer + + +class CommentListView(CommentViewMixin, generics.ListAPIView): + """View for retrieving list of comments.""" + permission_classes = (AllowAny, ) + + def get_queryset(self): + """Override get_queryset method.""" + return self.queryset.annotate_is_mine_status(user=self.request.user) + + +class CommentCreateView(CommentViewMixin, generics.CreateAPIView): + """View for create new comment.""" + serializer_class = serializers.EstablishmentCommentCreateSerializer + + +class CommentRUD(CommentViewMixin, generics.RetrieveUpdateDestroyAPIView): + """View for retrieve/update/destroy view.""" + + def get_queryset(self): + """Override get_queryset method.""" + return self.queryset.by_user(self.request.user) diff --git a/apps/comment/views/mobile.py b/apps/comment/views/mobile.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/comment/views/web.py b/apps/comment/views/web.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/establishment/admin.py b/apps/establishment/admin.py index 05ab36dd..19b34cdd 100644 --- a/apps/establishment/admin.py +++ b/apps/establishment/admin.py @@ -1,10 +1,12 @@ """Establishment admin conf.""" from django.contrib import admin from django.contrib.contenttypes.admin import GenericTabularInline +from django.utils.translation import gettext_lazy as _ + +from comment.models import Comment from establishment import models from main.models import Award, MetaDataContent from review import models as review_models -from django.utils.translation import gettext_lazy as _ @admin.register(models.EstablishmentType) @@ -44,13 +46,18 @@ class ReviewInline(GenericTabularInline): extra = 0 +class CommentInline(GenericTabularInline): + model = Comment + extra = 0 + + @admin.register(models.Establishment) class EstablishmentAdmin(admin.ModelAdmin): """Establishment admin.""" inlines = [ AwardInline, MetaDataContentInline, ContactPhoneInline, ContactEmailInline, - ReviewInline] + ReviewInline, CommentInline] @admin.register(models.EstablishmentSchedule) @@ -58,11 +65,6 @@ class EstablishmentSchedule(admin.ModelAdmin): """Establishment schedule""" -@admin.register(models.Comment) -class EstablishmentComment(admin.ModelAdmin): - """Establishment comments.""" - - @admin.register(models.Position) class PositionAdmin(admin.ModelAdmin): """Position admin.""" diff --git a/apps/establishment/migrations/0015_delete_comment.py b/apps/establishment/migrations/0015_delete_comment.py new file mode 100644 index 00000000..4b1cc007 --- /dev/null +++ b/apps/establishment/migrations/0015_delete_comment.py @@ -0,0 +1,16 @@ +# Generated by Django 2.2.4 on 2019-09-04 13:12 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0014_establishment_website'), + ] + + operations = [ + migrations.DeleteModel( + name='Comment', + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index a27a83f7..7ab4bcfe 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -1,11 +1,13 @@ """Establishment models.""" from functools import reduce + from django.contrib.contenttypes import fields as generic from django.core.exceptions import ValidationError from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField + from location.models import Address from utils.models import (ProjectBaseMixin, ImageMixin, TJSONField, TranslatedFieldsMixin, BaseAttributes) @@ -129,6 +131,7 @@ class Establishment(ProjectBaseMixin, ImageMixin, TranslatedFieldsMixin): awards = generic.GenericRelation(to='main.Award') tags = generic.GenericRelation(to='main.MetaDataContent') reviews = generic.GenericRelation(to='review.Review') + comments = generic.GenericRelation(to='comment.Comment') objects = EstablishmentQuerySet.as_manager() @@ -345,38 +348,3 @@ class Menu(TranslatedFieldsMixin, BaseAttributes): class Meta: verbose_name = _('menu') verbose_name_plural = _('menu') - - -class CommentQuerySet(models.QuerySet): - """QuerySets for Comment model.""" - - def by_author(self, author): - """Return comments by author""" - return self.filter(author=author) - - -class Comment(ProjectBaseMixin): - """Comment model.""" - text = models.TextField(verbose_name=_('Comment text')) - mark = models.PositiveIntegerField(blank=True, null=True, - default=None, - verbose_name=_('Mark')) - author = models.ForeignKey('account.User', - related_name='comments', - on_delete=models.CASCADE, - verbose_name=_('Author')) - establishment = models.ForeignKey(Establishment, - related_name='comments', - on_delete=models.CASCADE, - verbose_name=_('Establishment')) - - objects = CommentQuerySet.as_manager() - - class Meta: - """Meta class""" - verbose_name = _('Comment') - verbose_name_plural = _('Comments') - - def __str__(self): - """String representation""" - return str(self.author) diff --git a/apps/establishment/serializers.py b/apps/establishment/serializers.py index ec4fb43e..0e30f402 100644 --- a/apps/establishment/serializers.py +++ b/apps/establishment/serializers.py @@ -1,6 +1,7 @@ """Establishment serializers.""" from rest_framework import serializers +from comment.serializers.common import CommentSerializer from establishment import models from location.serializers import AddressSerializer from main.serializers import MetaDataContentSerializer, AwardSerializer, CurrencySerializer @@ -106,23 +107,6 @@ class ReviewSerializer(serializers.ModelSerializer): ) -class CommentSerializer(serializers.ModelSerializer): - """Comment serializer""" - nickname = serializers.CharField(source='author.username') - profile_pic = serializers.ImageField(source='author.image') - - class Meta: - """Serializer for model Comment""" - model = models.Comment - fields = ( - 'created', - 'text', - 'mark', - 'nickname', - 'profile_pic' - ) - - class EstablishmentEmployeeSerializer(serializers.ModelSerializer): """Serializer for actual employees.""" @@ -154,7 +138,8 @@ class EstablishmentSerializer(serializers.ModelSerializer): phones = ContactPhonesSerializer(read_only=True, many=True, ) emails = ContactEmailsSerializer(read_only=True, many=True, ) reviews = ReviewSerializer(source='reviews.last', allow_null=True) - comments = CommentSerializer(many=True, allow_null=True) + # comments = CommentSerializer(many=True, allow_null=True) + comments = serializers.SerializerMethodField() employees = EstablishmentEmployeeSerializer(source='actual_establishment_employees', many=True) menu = MenuSerializers(source='menu_set', many=True, read_only=True) @@ -198,6 +183,15 @@ class EstablishmentSerializer(serializers.ModelSerializer): 'best_price_carte' ) + def get_comments(self, obj): + """Serializer method for comment field""" + request = self.context.get('request') + if request.user.is_authenticated: + return CommentSerializer(obj.comments.all().annotate_is_mine_status(user=request.user), + many=True).data + else: + return CommentSerializer(obj.comments.all(), many=True).data + def get_preview_image(self, obj): """Get preview image""" return obj.get_full_image_url(request=self.context.get('request'), diff --git a/project/settings/base.py b/project/settings/base.py index ff6e6b66..94827649 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -67,6 +67,7 @@ PROJECT_APPS = [ 'configuration.apps.ConfigurationConfig', 'timetable.apps.TimetableConfig', 'review.apps.ReviewConfig', + 'comment.apps.CommentConfig', ] EXTERNAL_APPS = [ diff --git a/project/urls/web.py b/project/urls/web.py index e3b7b230..b7f67722 100644 --- a/project/urls/web.py +++ b/project/urls/web.py @@ -28,4 +28,5 @@ urlpatterns = [ path('location/', include('location.urls')), path('main/', include('main.urls')), path('translation/', include('translation.urls')), + path('comments/', include('comment.urls.web')), ]