From f18c18110cdb46ebc56f8c01b2e65741cc6959e9 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 25 Sep 2019 15:32:45 +0300 Subject: [PATCH 01/45] Refactor some establishment serializer --- apps/establishment/models.py | 31 ++++++++--- apps/establishment/serializers/back.py | 9 ++- apps/establishment/serializers/common.py | 71 +++++++++++------------- apps/establishment/views/back.py | 11 +++- apps/establishment/views/common.py | 15 ----- apps/establishment/views/web.py | 41 +++++++++----- apps/location/serializers/common.py | 17 +++++- 7 files changed, 112 insertions(+), 83 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index e3384dfb..4ab2fe24 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -13,8 +13,9 @@ from phonenumber_field.modelfields import PhoneNumberField from location.models import Address from collection.models import Collection +from main.models import MetaDataContent from review.models import Review -from utils.models import (ProjectBaseMixin, ImageMixin, TJSONField, URLImageMixin, +from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, TranslatedFieldsMixin, BaseAttributes) @@ -70,6 +71,20 @@ class EstablishmentSubType(ProjectBaseMixin, TranslatedFieldsMixin): class EstablishmentQuerySet(models.QuerySet): """Extended queryset for Establishment model.""" + def with_base_related(self): + """Return qs with related objects.""" + return self.select_related('address').prefetch_related( + models.Prefetch('tags', + MetaDataContent.objects.select_related( + 'metadata__category')) + ) + + def with_extended_related(self): + return self.select_related('establishment_type').\ + prefetch_related('establishment_subtypes', 'awards', 'schedule', + 'phones').\ + prefetch_actual_employees() + def search(self, value, locale=None): """Search text in JSON fields.""" if locale is not None: @@ -228,7 +243,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): name = models.CharField(_('name'), max_length=255, default='') name_translated = models.CharField(_('Transliterated name'), - max_length=255, default='') + max_length=255, default='') description = TJSONField(blank=True, null=True, default=None, verbose_name=_('description'), help_text='{"en-GB":"some text"}') @@ -312,12 +327,12 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): return country.low_price, country.high_price # todo: make via prefetch - @property - def subtypes(self): - return EstablishmentSubType.objects.filter( - subtype_establishment=self, - establishment_type=self.establishment_type, - establishment_type__use_subtypes=True) + # @property + # def subtypes(self): + # return EstablishmentSubType.objects.filter( + # subtype_establishment=self, + # establishment_type=self.establishment_type, + # establishment_type__use_subtypes=True) def set_establishment_type(self, establishment_type): self.establishment_type = establishment_type diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 7013fe6a..17d5f1d5 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -1,12 +1,9 @@ -import json from rest_framework import serializers - from establishment import models -from timetable.models import Timetable from establishment.serializers import ( EstablishmentBaseSerializer, PlateSerializer, ContactEmailsSerializer, - ContactPhonesSerializer, SocialNetworkRelatedSerializers, EstablishmentDetailSerializer -) + ContactPhonesSerializer, SocialNetworkRelatedSerializers, + EstablishmentTypeSerializer) from main.models import Currency @@ -21,6 +18,7 @@ class EstablishmentListCreateSerializer(EstablishmentBaseSerializer): emails = ContactEmailsSerializer(read_only=True, many=True, ) socials = SocialNetworkRelatedSerializers(read_only=True, many=True, ) slug = serializers.SlugField(required=True, allow_blank=False, max_length=50) + type = EstablishmentTypeSerializer(source='establishment_type') class Meta: model = models.Establishment @@ -52,6 +50,7 @@ class EstablishmentRUDSerializer(EstablishmentBaseSerializer): phones = ContactPhonesSerializer(read_only=False, many=True, ) emails = ContactEmailsSerializer(read_only=False, many=True, ) socials = SocialNetworkRelatedSerializers(read_only=False, many=True, ) + type = EstablishmentTypeSerializer(source='establishment_type') class Meta: model = models.Establishment diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 7b974887..f1b2d5ed 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -1,17 +1,17 @@ """Establishment serializers.""" from rest_framework import serializers - +from django.utils.translation import gettext_lazy as _ from comment import models as comment_models from comment.serializers import common as comment_serializers from establishment import models from favorites.models import Favorites -from location.serializers import AddressSerializer +from location.serializers import AddressSimpleSerializer from main.models import MetaDataContent from main.serializers import MetaDataContentSerializer, AwardSerializer, CurrencySerializer from review import models as review_models from timetable.serialziers import ScheduleRUDSerializer from utils import exceptions as utils_exceptions -from django.utils.translation import gettext_lazy as _ +from utils.serializers import TranslatedField class ContactPhonesSerializer(serializers.ModelSerializer): @@ -142,11 +142,11 @@ class EstablishmentEmployeeSerializer(serializers.ModelSerializer): class EstablishmentBaseSerializer(serializers.ModelSerializer): """Base serializer for Establishment model.""" - type = EstablishmentTypeSerializer(source='establishment_type', read_only=True) - subtypes = EstablishmentSubTypeSerializer(many=True) - address = AddressSerializer() - tags = MetaDataContentSerializer(many=True) + + preview_image = serializers.URLField(source='preview_image_url') slug = serializers.SlugField(allow_blank=False, required=True, max_length=50) + address = AddressSimpleSerializer() + tags = MetaDataContentSerializer(many=True) class Meta: """Meta class.""" @@ -159,60 +159,53 @@ class EstablishmentBaseSerializer(serializers.ModelSerializer): 'price_level', 'toque_number', 'public_mark', - 'type', - 'subtypes', + 'slug', + 'preview_image', + 'in_favorites', 'address', 'tags', - 'slug', ] class EstablishmentListSerializer(EstablishmentBaseSerializer): """Serializer for Establishment model.""" - # Annotated fields + in_favorites = serializers.BooleanField(allow_null=True) - preview_image = serializers.URLField(source='preview_image_url') - - class Meta: + class Meta(EstablishmentBaseSerializer.Meta): """Meta class.""" - model = models.Establishment fields = EstablishmentBaseSerializer.Meta.fields + [ 'in_favorites', - 'preview_image', ] class EstablishmentDetailSerializer(EstablishmentListSerializer): """Serializer for Establishment model.""" - description_translated = serializers.CharField(allow_null=True) + + description_translated = TranslatedField() + image = serializers.URLField(source='image_url') + type = EstablishmentTypeSerializer(source='establishment_type', read_only=True) + subtypes = EstablishmentSubTypeSerializer(many=True, source='establishment_subtypes') awards = AwardSerializer(many=True) schedule = ScheduleRUDSerializer(many=True, allow_null=True) - phones = ContactPhonesSerializer(read_only=True, many=True, ) - emails = ContactEmailsSerializer(read_only=True, many=True, ) + phones = ContactPhonesSerializer(read_only=True, many=True) + emails = ContactEmailsSerializer(read_only=True, many=True) review = serializers.SerializerMethodField() employees = EstablishmentEmployeeSerializer(source='actual_establishment_employees', many=True) menu = MenuSerializers(source='menu_set', many=True, read_only=True) - best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) - slug = serializers.SlugField(required=True, allow_blank=False, max_length=50) - - in_favorites = serializers.SerializerMethodField() - - image = serializers.URLField(source='image_url') - - class Meta: + class Meta(EstablishmentListSerializer.Meta): """Meta class.""" - model = models.Establishment fields = EstablishmentListSerializer.Meta.fields + [ 'description_translated', - 'price_level', 'image', + 'subtypes', + 'type', 'awards', 'schedule', 'website', @@ -228,23 +221,23 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer): 'best_price_menu', 'best_price_carte', 'transportation', - 'slug', ] + # todo: refactor this s (make as prefetch to model as attr or make as model property) def get_review(self, obj): """Serializer method for getting last published review""" return ReviewSerializer(obj.reviews.by_status(status=review_models.Review.READY) .order_by('-published_at').first()).data - def get_in_favorites(self, obj): - """Get in_favorites status flag""" - user = self.context.get('request').user - if user.is_authenticated: - return obj.id in user.favorites.by_content_type(app_label='establishment', - model='establishment')\ - .values_list('object_id', flat=True) - else: - return False + # def get_in_favorites(self, obj): + # """Get in_favorites status flag""" + # user = self.context.get('request').user + # if user.is_authenticated: + # return obj.id in user.favorites.by_content_type(app_label='establishment', + # model='establishment')\ + # .values_list('object_id', flat=True) + # else: + # return False class EstablishmentCommentCreateSerializer(comment_serializers.CommentSerializer): diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 974fca8a..78526bdd 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -4,10 +4,17 @@ from rest_framework import generics from establishment import models from establishment import serializers -from establishment.views.common import EstablishmentMixin -class EstablishmentListCreateView(EstablishmentMixin, generics.ListCreateAPIView): +class EstablishmentMixinViews: + """Establishment mixin.""" + + def get_queryset(self): + """Overrided method 'get_queryset'.""" + return models.Establishment.objects.published().with_related() + + +class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAPIView): """Establishment list/create view.""" queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentListCreateSerializer diff --git a/apps/establishment/views/common.py b/apps/establishment/views/common.py index b57d6ef6..3029c080 100644 --- a/apps/establishment/views/common.py +++ b/apps/establishment/views/common.py @@ -1,16 +1 @@ """Establishment app views.""" - -from rest_framework import permissions - -from establishment import models - - -class EstablishmentMixin: - """Establishment mixin.""" - - permission_classes = (permissions.AllowAny,) - - def get_queryset(self): - """Overrided method 'get_queryset'.""" - return models.Establishment.objects.published() \ - .prefetch_actual_employees() diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index ce2bed05..3748b95f 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -1,27 +1,47 @@ """Establishment app views.""" - from django.contrib.gis.geos import Point from django.shortcuts import get_object_or_404 from rest_framework import generics, permissions from comment import models as comment_models -from establishment import filters -from establishment import models, serializers -from establishment.views import EstablishmentMixin +from establishment import filters, models, serializers from main.models import MetaDataContent from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer -class EstablishmentListView(EstablishmentMixin, generics.ListAPIView): +class EstablishmentMixinView: + """Establishment mixin.""" + + permission_classes = (permissions.AllowAny,) + + def get_queryset(self): + """Overrided method 'get_queryset'.""" + return models.Establishment.objects.published().with_base_related().\ + annotate_in_favorites(self.request.user) + + +class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView): """Resource for getting a list of establishments.""" - serializer_class = serializers.EstablishmentListSerializer + filter_class = filters.EstablishmentFilter + serializer_class = serializers.EstablishmentListSerializer def get_queryset(self): """Overridden method 'get_queryset'.""" qs = super(EstablishmentListView, self).get_queryset() - return qs.by_country_code(code=self.request.country_code) \ - .annotate_in_favorites(user=self.request.user) + if self.request.country_code: + qs = qs.by_country_code(self.request.country_code) + return qs + + +class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView): + """Resource for getting a establishment.""" + + lookup_field = 'slug' + serializer_class = serializers.EstablishmentDetailSerializer + + def get_queryset(self): + return super().get_queryset().with_extended_related() class EstablishmentSimilarListView(EstablishmentListView): @@ -33,11 +53,6 @@ class EstablishmentSimilarListView(EstablishmentListView): return super().get_queryset().similar(establishment_slug=self.kwargs.get('slug'))\ .order_by('-total_mark')[:13] -class EstablishmentRetrieveView(EstablishmentMixin, generics.RetrieveAPIView): - """Resource for getting a establishment.""" - lookup_field = 'slug' - serializer_class = serializers.EstablishmentDetailSerializer - class EstablishmentTypeListView(generics.ListAPIView): """Resource for getting a list of establishment types.""" diff --git a/apps/location/serializers/common.py b/apps/location/serializers/common.py index 555cf499..1dee92ed 100644 --- a/apps/location/serializers/common.py +++ b/apps/location/serializers/common.py @@ -1,7 +1,6 @@ """Location app common serializers.""" from django.contrib.gis.geos import Point from rest_framework import serializers - from location import models from utils.serializers import TranslatedField @@ -86,6 +85,7 @@ class CitySerializer(serializers.ModelSerializer): class AddressSerializer(serializers.ModelSerializer): """Address serializer.""" + city_id = serializers.PrimaryKeyRelatedField( source='city', queryset=models.City.objects.all()) @@ -128,3 +128,18 @@ class AddressSerializer(serializers.ModelSerializer): setattr(instance, 'geo_lon', float(0)) return super().to_representation(instance) + +class AddressSimpleSerializer(serializers.ModelSerializer): + """Serializer for address obj in related objects.""" + + class Meta: + """Meta class.""" + + model = models.Address + fields = ( + 'id', + 'street_name_1', + 'street_name_2', + 'number', + 'postal_code', + ) From 6ea876cdc60b6a6022b8f7b48661ada8166f6071 Mon Sep 17 00:00:00 2001 From: Dmitriy Kuzmenko Date: Thu, 26 Sep 2019 11:39:48 +0300 Subject: [PATCH 02/45] test small fix --- apps/collection/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/collection/tests.py b/apps/collection/tests.py index 6a985fc0..4d5ba4ce 100644 --- a/apps/collection/tests.py +++ b/apps/collection/tests.py @@ -20,7 +20,7 @@ class BaseTestCase(APITestCase): self.newsletter = True self.user = User.objects.create_user( username=self.username, email=self.email, password=self.password) - #get tokens + # get tokens tokens = User.create_jwt_tokens(self.user) self.client.cookies = SimpleCookie( {'access_token': tokens.get('access_token'), From 5793eaad32db7cf90341d2b3d0e349e5a925c949 Mon Sep 17 00:00:00 2001 From: littlewolf Date: Thu, 26 Sep 2019 20:17:50 +0300 Subject: [PATCH 03/45] Add queryset mixin Switch view in urls/common Add homepage view Switch test --- apps/collection/models.py | 4 +++- apps/collection/tests.py | 22 ++++++++++++++++++++++ apps/collection/urls/common.py | 2 +- apps/collection/views/common.py | 24 +++++++++++++++++++++--- apps/utils/querysets.py | 29 +++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 5 deletions(-) diff --git a/apps/collection/models.py b/apps/collection/models.py index beec9b08..25bf1ef9 100644 --- a/apps/collection/models.py +++ b/apps/collection/models.py @@ -8,6 +8,8 @@ from django.utils.translation import gettext_lazy as _ from utils.models import ProjectBaseMixin, URLImageMixin from utils.models import TranslatedFieldsMixin +from utils.querysets import RelatedObjectsCountMixin + # Mixins class CollectionNameMixin(models.Model): @@ -30,7 +32,7 @@ class CollectionDateMixin(models.Model): # Models -class CollectionQuerySet(models.QuerySet): +class CollectionQuerySet(RelatedObjectsCountMixin): """QuerySet for model Collection""" def by_country_code(self, code): diff --git a/apps/collection/tests.py b/apps/collection/tests.py index 6a985fc0..ceb08477 100644 --- a/apps/collection/tests.py +++ b/apps/collection/tests.py @@ -8,6 +8,7 @@ from http.cookies import SimpleCookie from collection.models import Collection, Guide from location.models import Country +from establishment.models import Establishment, EstablishmentType # Create your tests here. @@ -81,3 +82,24 @@ class CollectionGuideDetailTests(CollectionDetailTests): def test_guide_detail_Read(self): response = self.client.get(f'/api/web/collections/guides/{self.guide.id}/', format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) + + +class CollectionWebHomeTests(CollectionDetailTests): + def setUp(self): + super().setUp() + self.establishment_type = EstablishmentType.objects.create(name="Test establishment type") + + self.establishment = Establishment.objects.create( + name="Test establishment", + establishment_type_id=self.establishment_type.id, + is_publish=True, + slug="test" + ) + + self.establishment.collections.add(self.collection) + self.establishment.save() + + def test_collection_list_filter(self): + print("========================================================================================") + response = self.client.get('/api/web/collections/', format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/apps/collection/urls/common.py b/apps/collection/urls/common.py index 01385c3d..7ffa50cf 100644 --- a/apps/collection/urls/common.py +++ b/apps/collection/urls/common.py @@ -6,7 +6,7 @@ from collection.views import common as views app_name = 'collection' urlpatterns = [ - path('', views.CollectionListView.as_view(), name='list'), + path('', views.CollectionHomePageView.as_view(), name='list'), path('/establishments/', views.CollectionEstablishmentListView.as_view(), name='detail'), diff --git a/apps/collection/views/common.py b/apps/collection/views/common.py index d42bd851..0818c369 100644 --- a/apps/collection/views/common.py +++ b/apps/collection/views/common.py @@ -30,9 +30,27 @@ class CollectionListView(CollectionViewMixin, generics.ListAPIView): def get_queryset(self): """Override get_queryset method""" - return models.Collection.objects.published()\ - .by_country_code(code=self.request.country_code)\ - .order_by('-on_top', '-created') + queryset = models.Collection.objects.published()\ + .by_country_code(code=self.request.country_code)\ + .order_by('-on_top', '-created') + + return queryset + + +class CollectionHomePageView(CollectionViewMixin, generics.ListAPIView): + """List Collection view""" + permission_classes = (permissions.AllowAny,) + serializer_class = serializers.CollectionSerializer + + def get_queryset(self): + """Override get_queryset method""" + queryset = models.Collection.objects.published()\ + .by_country_code(code=self.request.country_code)\ + .annotate_related_objects_count() \ + .filter_related_gt(3) \ + .order_by('-on_top', '-created') + + return queryset class CollectionEstablishmentListView(CollectionListView): diff --git a/apps/utils/querysets.py b/apps/utils/querysets.py index f25507f7..b6d118a5 100644 --- a/apps/utils/querysets.py +++ b/apps/utils/querysets.py @@ -1,5 +1,6 @@ """Utils QuerySet Mixins""" from django.db import models +from django.db.models import Q from utils.methods import get_contenttype @@ -15,3 +16,31 @@ class ContentTypeQuerySetMixin(models.QuerySet): """Filter QuerySet by ContentType.""" return self.filter(content_type=get_contenttype(app_label=app_label, model=model)) + + +class RelatedObjectsCountMixin(models.QuerySet): + """QuerySet for ManyToMany related count filter""" + + def get_related_objects_names(self): + related_objects_names = [] + + for related_object in self.model._meta.related_objects: + if isinstance(related_object, models.ManyToManyRel): + related_objects_names.append(related_object.name) + + return related_objects_names + + def annotate_related_objects_count(self): + + annotations = {} + for related_object in self.get_related_objects_names(): + annotations[f"{related_object}_count"] = models.Count(f"{related_object}") + + return self.annotate(**annotations) + + def filter_related_gt(self, count): + q_objects = Q() + for related_object in self.get_related_objects_names(): + q_objects.add(Q(**{f"{related_object}_count__gt": count}), Q.OR) + + return self.filter(q_objects) From 4f2bca78f9dc987e6ba17847fc19638681e54513 Mon Sep 17 00:00:00 2001 From: littlewolf Date: Fri, 27 Sep 2019 07:52:57 +0300 Subject: [PATCH 04/45] Update tests Update view --- apps/collection/tests.py | 25 ++++++++++++++----------- apps/collection/views/common.py | 4 ++-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/collection/tests.py b/apps/collection/tests.py index ceb08477..54d7917c 100644 --- a/apps/collection/tests.py +++ b/apps/collection/tests.py @@ -89,17 +89,20 @@ class CollectionWebHomeTests(CollectionDetailTests): super().setUp() self.establishment_type = EstablishmentType.objects.create(name="Test establishment type") - self.establishment = Establishment.objects.create( - name="Test establishment", - establishment_type_id=self.establishment_type.id, - is_publish=True, - slug="test" - ) + for i in range(1, 5): + setattr(self, f"establishment{i}", + Establishment.objects.create( + name=f"Test establishment {i}", + establishment_type_id=self.establishment_type.id, + is_publish=True, + slug=f"test-establishment-{i}" + ) + ) - self.establishment.collections.add(self.collection) - self.establishment.save() + getattr(self, f"establishment{i}").collections.add(self.collection) def test_collection_list_filter(self): - print("========================================================================================") - response = self.client.get('/api/web/collections/', format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) + response = self.client.get('/api/web/collections/?country_code=en', format='json') + data = response.json() + self.assertIn('count', data) + self.assertGreater(data['count'], 0) diff --git a/apps/collection/views/common.py b/apps/collection/views/common.py index 0818c369..662d35b5 100644 --- a/apps/collection/views/common.py +++ b/apps/collection/views/common.py @@ -46,8 +46,8 @@ class CollectionHomePageView(CollectionViewMixin, generics.ListAPIView): """Override get_queryset method""" queryset = models.Collection.objects.published()\ .by_country_code(code=self.request.country_code)\ - .annotate_related_objects_count() \ - .filter_related_gt(3) \ + .annotate_related_objects_count()\ + .filter_related_gt(3)\ .order_by('-on_top', '-created') return queryset From 44f792db0cd20a427779204317d88d627b59e065 Mon Sep 17 00:00:00 2001 From: littlewolf Date: Fri, 27 Sep 2019 07:58:21 +0300 Subject: [PATCH 05/45] Switch sorting --- apps/collection/views/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/collection/views/common.py b/apps/collection/views/common.py index 662d35b5..f41019ae 100644 --- a/apps/collection/views/common.py +++ b/apps/collection/views/common.py @@ -48,7 +48,7 @@ class CollectionHomePageView(CollectionViewMixin, generics.ListAPIView): .by_country_code(code=self.request.country_code)\ .annotate_related_objects_count()\ .filter_related_gt(3)\ - .order_by('-on_top', '-created') + .order_by('-on_top', '-modified') return queryset From 9608fdb92a45bfdced27d48f1919e14c069e49cb Mon Sep 17 00:00:00 2001 From: michail Date: Fri, 27 Sep 2019 11:30:58 +0500 Subject: [PATCH 06/45] property was added to news model --- apps/news/models.py | 19 +++++++++++++++++++ apps/news/serializers.py | 1 + 2 files changed, 20 insertions(+) diff --git a/apps/news/models.py b/apps/news/models.py index 140c89c9..75deee56 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -5,6 +5,7 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ from rest_framework.reverse import reverse from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin +from django.contrib.contenttypes.models import ContentType class NewsType(models.Model): @@ -100,3 +101,21 @@ class News(BaseAttributes, TranslatedFieldsMixin): @property def web_url(self): return reverse('web:news:rud', kwargs={'slug': self.slug}) + + @property + def list_also_like_news(self): + # news_content_type = ContentType.objects.get(app_label="news", model="news") + + # tg_name = self.tags.filter(metadata__content_type=news_content_type) + + # print(tg_name) + + # tag_id = self.tags.all() + # + like_news = News.objects.filter(is_publish=True, news_type=self.news_type, tags__id=self.tags__id) + # + # print("LINE 112", like_news) + + news_list = [self.description, "extra", "field", "test", self.slug] + + return news_list diff --git a/apps/news/serializers.py b/apps/news/serializers.py index dbcd0f62..981ddae0 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -64,6 +64,7 @@ class NewsDetailSerializer(NewsBaseSerializer): 'is_publish', 'author', 'country', + 'list_also_like_news', ) From 962dc2f480b7c1e79808fbe4e0fc48c6d90c80b8 Mon Sep 17 00:00:00 2001 From: michail Date: Fri, 27 Sep 2019 12:01:30 +0500 Subject: [PATCH 07/45] maybe it is solution --- apps/news/models.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/news/models.py b/apps/news/models.py index 75deee56..1b9023db 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -111,11 +111,16 @@ class News(BaseAttributes, TranslatedFieldsMixin): # print(tg_name) # tag_id = self.tags.all() + news_content_type = ContentType.objects.get(app_label="news", model="news") # - like_news = News.objects.filter(is_publish=True, news_type=self.news_type, tags__id=self.tags__id) + like_news = News.objects.filter(is_publish=True, news_type=self.news_type, + tags__content_type=news_content_type) + # # print("LINE 112", like_news) - news_list = [self.description, "extra", "field", "test", self.slug] + # news_list = [self.description, "extra", "field", "test", self.slug] + + news_list = [{"id": n.id, "slug": n.slug} for n in like_news] return news_list From aa037d27f563ecc23e46f398a88f590485f56b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 27 Sep 2019 10:41:41 +0300 Subject: [PATCH 08/45] Double api --- apps/search_indexes/urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/search_indexes/urls.py b/apps/search_indexes/urls.py index 664e0b99..8e914719 100644 --- a/apps/search_indexes/urls.py +++ b/apps/search_indexes/urls.py @@ -6,5 +6,6 @@ from search_indexes import views router = routers.SimpleRouter() # router.register(r'news', views.NewsDocumentViewSet, basename='news') # temporarily disabled router.register(r'establishments', views.EstablishmentDocumentViewSet, basename='establishment') +router.register(r'mobile/establishments', views.EstablishmentDocumentViewSet, basename='establishment-mobile') urlpatterns = router.urls From d115d30e821e5413c7d9bb909ac013e977fcba5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 27 Sep 2019 11:19:49 +0300 Subject: [PATCH 09/45] Fix test authorization --- apps/authorization/tests/tests_authorization.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/authorization/tests/tests_authorization.py b/apps/authorization/tests/tests_authorization.py index 4a5d2a2b..d9fd7b71 100644 --- a/apps/authorization/tests/tests_authorization.py +++ b/apps/authorization/tests/tests_authorization.py @@ -22,7 +22,6 @@ def get_tokens_for_user( class AuthorizationTests(APITestCase): def setUp(self): - print("Auth!") data = get_tokens_for_user() self.username = data["username"] self.password = data["password"] From 993e58d215e664b2ef191c9bfbf9f24601dbac40 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Fri, 27 Sep 2019 11:20:43 +0300 Subject: [PATCH 10/45] fix views after merge --- apps/establishment/views/back.py | 2 +- apps/establishment/views/web.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 78526bdd..5cba8255 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -11,7 +11,7 @@ class EstablishmentMixinViews: def get_queryset(self): """Overrided method 'get_queryset'.""" - return models.Establishment.objects.published().with_related() + return models.Establishment.objects.published().with_base_related() class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAPIView): diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index bda64dfb..4ece55c0 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -7,7 +7,6 @@ from rest_framework import generics, permissions from comment import models as comment_models from establishment import filters from establishment import models, serializers -from establishment.views import EstablishmentMixin from main import methods from main.models import MetaDataContent from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer From c7974a75115dae7b7662116d1918e20a66fed644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 27 Sep 2019 11:33:49 +0300 Subject: [PATCH 11/45] Refactor authorization test --- apps/authorization/tests/tests_authorization.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/authorization/tests/tests_authorization.py b/apps/authorization/tests/tests_authorization.py index d9fd7b71..6028e386 100644 --- a/apps/authorization/tests/tests_authorization.py +++ b/apps/authorization/tests/tests_authorization.py @@ -1,5 +1,6 @@ from rest_framework.test import APITestCase from account.models import User +from django.urls import reverse # Create your tests here. @@ -32,7 +33,9 @@ class AuthorizationTests(APITestCase): 'password': self.password, 'remember': True } - response = self.client.post('/api/auth/login/', data=data) + # login + # /api/auth/login/ + response = self.client.post(reverse('auth:authorization:login'), data=data) self.assertEqual(response.data['access_token'], self.tokens.get('access_token')) self.assertEqual(response.data['refresh_token'], self.tokens.get('refresh_token')) From 62fbded6dea37338579948701a0e8c6d7c7bf19c Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Fri, 27 Sep 2019 11:41:13 +0300 Subject: [PATCH 12/45] added todo --- apps/utils/middleware.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/utils/middleware.py b/apps/utils/middleware.py index 096f8474..f3936169 100644 --- a/apps/utils/middleware.py +++ b/apps/utils/middleware.py @@ -19,6 +19,7 @@ def parse_cookies(get_response): cookie_dict = request.COOKIES # processing locale cookie locale = get_locale(cookie_dict) + # todo: don't use DB!!! Use cache if not Language.objects.filter(locale=locale).exists(): locale = TranslationSettings.get_solo().default_language translation.activate(locale) From fc5ec0920ec3aa5e409d0461cd5c6a109578c488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 27 Sep 2019 11:43:14 +0300 Subject: [PATCH 13/45] fix --- apps/authorization/tests/tests_authorization.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/authorization/tests/tests_authorization.py b/apps/authorization/tests/tests_authorization.py index 6028e386..a6a49ea5 100644 --- a/apps/authorization/tests/tests_authorization.py +++ b/apps/authorization/tests/tests_authorization.py @@ -33,8 +33,6 @@ class AuthorizationTests(APITestCase): 'password': self.password, 'remember': True } - # login - # /api/auth/login/ response = self.client.post(reverse('auth:authorization:login'), data=data) self.assertEqual(response.data['access_token'], self.tokens.get('access_token')) self.assertEqual(response.data['refresh_token'], self.tokens.get('refresh_token')) From c9bf83fba727745505532d5970cfd40f2bf9767e Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Fri, 27 Sep 2019 11:53:40 +0300 Subject: [PATCH 14/45] News slug, disable null, remove editable --- .../migrations/0014_auto_20190927_0845.py | 21 +++++++++++++++++++ .../migrations/0015_auto_20190927_0853.py | 18 ++++++++++++++++ apps/news/models.py | 4 ++-- 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 apps/news/migrations/0014_auto_20190927_0845.py create mode 100644 apps/news/migrations/0015_auto_20190927_0853.py diff --git a/apps/news/migrations/0014_auto_20190927_0845.py b/apps/news/migrations/0014_auto_20190927_0845.py new file mode 100644 index 00000000..270296a4 --- /dev/null +++ b/apps/news/migrations/0014_auto_20190927_0845.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.4 on 2019-09-27 08:45 +from django.db import migrations + + +def fill_slug(apps,schemaeditor): + News = apps.get_model('news', 'News') + for news in News.objects.all(): + if news.slug is None: + news.slug = f'Slug {news.id}' + news.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0013_auto_20190924_0806'), + ] + + operations = [ + migrations.RunPython(fill_slug, migrations.RunPython.noop) + ] diff --git a/apps/news/migrations/0015_auto_20190927_0853.py b/apps/news/migrations/0015_auto_20190927_0853.py new file mode 100644 index 00000000..e756e2e5 --- /dev/null +++ b/apps/news/migrations/0015_auto_20190927_0853.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-09-27 08:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0014_auto_20190927_0845'), + ] + + operations = [ + migrations.AlterField( + model_name='news', + name='slug', + field=models.SlugField(unique=True, verbose_name='News slug'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 140c89c9..4fb8e118 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -63,8 +63,8 @@ class News(BaseAttributes, TranslatedFieldsMixin): start = models.DateTimeField(verbose_name=_('Start')) end = models.DateTimeField(blank=True, null=True, default=None, verbose_name=_('End')) - slug = models.SlugField(unique=True, max_length=50, null=True, - verbose_name=_('News slug'), editable=True,) + slug = models.SlugField(unique=True, max_length=50, + verbose_name=_('News slug')) playlist = models.IntegerField(_('playlist')) is_publish = models.BooleanField(default=False, verbose_name=_('Publish status')) From cd951ddd75f369721eb96d6d75caf4d5e4991a70 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Fri, 27 Sep 2019 11:57:42 +0300 Subject: [PATCH 15/45] remove custom slug field from serializer, small fix in migration --- apps/news/migrations/0014_auto_20190927_0845.py | 2 +- apps/news/serializers.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/news/migrations/0014_auto_20190927_0845.py b/apps/news/migrations/0014_auto_20190927_0845.py index 270296a4..dcdee4e4 100644 --- a/apps/news/migrations/0014_auto_20190927_0845.py +++ b/apps/news/migrations/0014_auto_20190927_0845.py @@ -6,7 +6,7 @@ def fill_slug(apps,schemaeditor): News = apps.get_model('news', 'News') for news in News.objects.all(): if news.slug is None: - news.slug = f'Slug {news.id}' + news.slug = f'Slug_{news.id}' news.save() diff --git a/apps/news/serializers.py b/apps/news/serializers.py index dbcd0f62..3d15cff9 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -28,8 +28,6 @@ class NewsBaseSerializer(serializers.ModelSerializer): news_type = NewsTypeSerializer(read_only=True) tags = MetaDataContentSerializer(read_only=True, many=True) - slug = serializers.SlugField(allow_blank=False, required=True, max_length=50) - class Meta: """Meta class.""" From 910322273e72b095862e6a8e8ff7ab23c3296084 Mon Sep 17 00:00:00 2001 From: littlewolf Date: Fri, 27 Sep 2019 12:01:11 +0300 Subject: [PATCH 16/45] Add annotate to filter --- apps/collection/views/common.py | 1 - apps/utils/querysets.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/collection/views/common.py b/apps/collection/views/common.py index f41019ae..80a21be4 100644 --- a/apps/collection/views/common.py +++ b/apps/collection/views/common.py @@ -46,7 +46,6 @@ class CollectionHomePageView(CollectionViewMixin, generics.ListAPIView): """Override get_queryset method""" queryset = models.Collection.objects.published()\ .by_country_code(code=self.request.country_code)\ - .annotate_related_objects_count()\ .filter_related_gt(3)\ .order_by('-on_top', '-modified') diff --git a/apps/utils/querysets.py b/apps/utils/querysets.py index b6d118a5..4937b867 100644 --- a/apps/utils/querysets.py +++ b/apps/utils/querysets.py @@ -43,4 +43,4 @@ class RelatedObjectsCountMixin(models.QuerySet): for related_object in self.get_related_objects_names(): q_objects.add(Q(**{f"{related_object}_count__gt": count}), Q.OR) - return self.filter(q_objects) + return self.annotate_related_objects_count().filter(q_objects) From 46ce25f0f767aafe59a0092e2cd21c8c12342ea3 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Fri, 27 Sep 2019 12:21:50 +0300 Subject: [PATCH 17/45] fix migration --- apps/news/migrations/0014_auto_20190927_0845.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/news/migrations/0014_auto_20190927_0845.py b/apps/news/migrations/0014_auto_20190927_0845.py index dcdee4e4..5a2b35fb 100644 --- a/apps/news/migrations/0014_auto_20190927_0845.py +++ b/apps/news/migrations/0014_auto_20190927_0845.py @@ -1,11 +1,12 @@ # Generated by Django 2.2.4 on 2019-09-27 08:45 from django.db import migrations +from django.core.validators import EMPTY_VALUES def fill_slug(apps,schemaeditor): News = apps.get_model('news', 'News') for news in News.objects.all(): - if news.slug is None: + if news.slug in EMPTY_VALUES: news.slug = f'Slug_{news.id}' news.save() From b55a8ffda6a35886976a971019e4617eca51f04e Mon Sep 17 00:00:00 2001 From: littlewolf Date: Fri, 27 Sep 2019 12:23:12 +0300 Subject: [PATCH 18/45] Add comments --- apps/utils/querysets.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/utils/querysets.py b/apps/utils/querysets.py index 4937b867..1c7dfa84 100644 --- a/apps/utils/querysets.py +++ b/apps/utils/querysets.py @@ -22,6 +22,7 @@ class RelatedObjectsCountMixin(models.QuerySet): """QuerySet for ManyToMany related count filter""" def get_related_objects_names(self): + """Get all related objects (with reversed)""" related_objects_names = [] for related_object in self.model._meta.related_objects: @@ -31,7 +32,7 @@ class RelatedObjectsCountMixin(models.QuerySet): return related_objects_names def annotate_related_objects_count(self): - + """Annotate all related objects to queryset""" annotations = {} for related_object in self.get_related_objects_names(): annotations[f"{related_object}_count"] = models.Count(f"{related_object}") @@ -39,6 +40,7 @@ class RelatedObjectsCountMixin(models.QuerySet): return self.annotate(**annotations) def filter_related_gt(self, count): + """QuerySet filter by related objects count""" q_objects = Q() for related_object in self.get_related_objects_names(): q_objects.add(Q(**{f"{related_object}_count__gt": count}), Q.OR) From 755490c6cf92dc170934bd0126ccbbbe96fdee1a Mon Sep 17 00:00:00 2001 From: michail Date: Fri, 27 Sep 2019 17:12:24 +0500 Subject: [PATCH 19/45] done --- apps/news/models.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/apps/news/models.py b/apps/news/models.py index 1b9023db..cfb10d39 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -5,7 +5,7 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ from rest_framework.reverse import reverse from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin -from django.contrib.contenttypes.models import ContentType +from random import sample as random_sample class NewsType(models.Model): @@ -104,23 +104,18 @@ class News(BaseAttributes, TranslatedFieldsMixin): @property def list_also_like_news(self): - # news_content_type = ContentType.objects.get(app_label="news", model="news") - # tg_name = self.tags.filter(metadata__content_type=news_content_type) + # without "distinct" method the doubles are arising + like_news = News.objects.published().filter(news_type=self.news_type, tags__in=models.F("tags"))\ + .exclude(id=self.id).distinct() - # print(tg_name) + news_count = like_news.count() - # tag_id = self.tags.all() - news_content_type = ContentType.objects.get(app_label="news", model="news") - # - like_news = News.objects.filter(is_publish=True, news_type=self.news_type, - tags__content_type=news_content_type) + if news_count >= 6: + random_ids = random_sample(range(news_count), 6) + else: + random_ids = random_sample(range(news_count), news_count) - # - # print("LINE 112", like_news) - - # news_list = [self.description, "extra", "field", "test", self.slug] - - news_list = [{"id": n.id, "slug": n.slug} for n in like_news] + news_list = [{"id": like_news[r].id, "slug": like_news[r].slug} for r in random_ids] return news_list From 785d2a2f0987ba09c1fd5907245d217097fc75d4 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Fri, 27 Sep 2019 16:56:26 +0300 Subject: [PATCH 20/45] update news serializer, RUD for backoffice --- apps/news/migrations/0016_news_template.py | 18 ++++++++++++++++++ apps/news/models.py | 12 ++++++++++++ apps/news/serializers.py | 5 +++++ 3 files changed, 35 insertions(+) create mode 100644 apps/news/migrations/0016_news_template.py diff --git a/apps/news/migrations/0016_news_template.py b/apps/news/migrations/0016_news_template.py new file mode 100644 index 00000000..f85959ba --- /dev/null +++ b/apps/news/migrations/0016_news_template.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-09-27 13:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0015_auto_20190927_0853'), + ] + + operations = [ + migrations.AddField( + model_name='news', + name='template', + field=models.PositiveIntegerField(choices=[(0, 'newspaper'), (1, 'main.pdf.erb'), (2, 'main')], default=0), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 4fb8e118..e0c5fbf4 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -49,6 +49,17 @@ class NewsQuerySet(models.QuerySet): class News(BaseAttributes, TranslatedFieldsMixin): """News model.""" + # template choices + NEWSPAPER = 0 + MAIN_PDF_ERB = 1 + MAIN = 2 + + TEMPLATE_CHOICES = ( + (NEWSPAPER, 'newspaper'), + (MAIN_PDF_ERB, 'main.pdf.erb'), + (MAIN, 'main'), + ) + news_type = models.ForeignKey(NewsType, on_delete=models.PROTECT, verbose_name=_('news type')) title = TJSONField(blank=True, null=True, default=None, @@ -78,6 +89,7 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('Image URL path')) preview_image_url = models.URLField(blank=True, null=True, default=None, verbose_name=_('Preview image URL path')) + template = models.PositiveIntegerField(choices=TEMPLATE_CHOICES, default=NEWSPAPER) address = models.ForeignKey('location.Address', blank=True, null=True, default=None, verbose_name=_('address'), on_delete=models.SET_NULL) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 3d15cff9..82f9980b 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -89,6 +89,9 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, source='country', write_only=True, queryset=location_models.Country.objects.all()) + template_display = serializers.CharField(source='get_template_display', + read_only=True) + class Meta(NewsBackOfficeBaseSerializer.Meta, NewsDetailSerializer.Meta): """Meta class.""" @@ -97,5 +100,7 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, 'description', 'news_type_id', 'country_id', + 'template', + 'template_display', ) From ae87a51277fdd38647f16598bc7a35d45ffbe676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 27 Sep 2019 17:10:08 +0300 Subject: [PATCH 21/45] Author --- apps/news/migrations/0016_remove_news_author.py | 17 +++++++++++++++++ apps/news/models.py | 4 ++-- apps/news/serializers.py | 3 ++- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 apps/news/migrations/0016_remove_news_author.py diff --git a/apps/news/migrations/0016_remove_news_author.py b/apps/news/migrations/0016_remove_news_author.py new file mode 100644 index 00000000..31ad12bb --- /dev/null +++ b/apps/news/migrations/0016_remove_news_author.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.4 on 2019-09-27 13:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0015_auto_20190927_0853'), + ] + + operations = [ + migrations.RemoveField( + model_name='news', + name='author', + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 4fb8e118..545de59d 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -68,8 +68,8 @@ class News(BaseAttributes, TranslatedFieldsMixin): playlist = models.IntegerField(_('playlist')) is_publish = models.BooleanField(default=False, verbose_name=_('Publish status')) - author = models.CharField(max_length=255, blank=True, null=True, - default=None,verbose_name=_('Author')) + # author = models.CharField(max_length=255, blank=True, null=True, + # default=None,verbose_name=_('Author')) is_highlighted = models.BooleanField(default=False, verbose_name=_('Is highlighted')) # TODO: metadata_keys - описание ключей для динамического построения полей метаданных diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 3d15cff9..1778d410 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -5,7 +5,7 @@ from location.serializers import CountrySimpleSerializer from main.serializers import MetaDataContentSerializer from news import models from utils.serializers import TranslatedField - +from account.serializers.common import UserSerializer class NewsTypeSerializer(serializers.ModelSerializer): """News type serializer.""" @@ -50,6 +50,7 @@ class NewsDetailSerializer(NewsBaseSerializer): description_translated = TranslatedField() country = CountrySimpleSerializer(read_only=True) + author = UserSerializer(source='news_records_created') class Meta(NewsBaseSerializer.Meta): """Meta class.""" From a0a6578ae849d5c0b9c96206b9e3ac8d77c0dbdc Mon Sep 17 00:00:00 2001 From: littlewolf Date: Fri, 27 Sep 2019 17:11:42 +0300 Subject: [PATCH 22/45] Add new filter --- apps/collection/views/common.py | 2 +- apps/utils/querysets.py | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/apps/collection/views/common.py b/apps/collection/views/common.py index 80a21be4..148c5fab 100644 --- a/apps/collection/views/common.py +++ b/apps/collection/views/common.py @@ -46,7 +46,7 @@ class CollectionHomePageView(CollectionViewMixin, generics.ListAPIView): """Override get_queryset method""" queryset = models.Collection.objects.published()\ .by_country_code(code=self.request.country_code)\ - .filter_related_gt(3)\ + .filter_all_related_gt(3)\ .order_by('-on_top', '-modified') return queryset diff --git a/apps/utils/querysets.py b/apps/utils/querysets.py index 1c7dfa84..b94cb7b3 100644 --- a/apps/utils/querysets.py +++ b/apps/utils/querysets.py @@ -1,7 +1,8 @@ """Utils QuerySet Mixins""" from django.db import models -from django.db.models import Q - +from django.db.models import Q, Sum, F +from functools import reduce +from operator import add from utils.methods import get_contenttype @@ -21,7 +22,7 @@ class ContentTypeQuerySetMixin(models.QuerySet): class RelatedObjectsCountMixin(models.QuerySet): """QuerySet for ManyToMany related count filter""" - def get_related_objects_names(self): + def _get_related_objects_names(self): """Get all related objects (with reversed)""" related_objects_names = [] @@ -31,10 +32,10 @@ class RelatedObjectsCountMixin(models.QuerySet): return related_objects_names - def annotate_related_objects_count(self): + def _annotate_related_objects_count(self): """Annotate all related objects to queryset""" annotations = {} - for related_object in self.get_related_objects_names(): + for related_object in self._get_related_objects_names(): annotations[f"{related_object}_count"] = models.Count(f"{related_object}") return self.annotate(**annotations) @@ -42,7 +43,13 @@ class RelatedObjectsCountMixin(models.QuerySet): def filter_related_gt(self, count): """QuerySet filter by related objects count""" q_objects = Q() - for related_object in self.get_related_objects_names(): + for related_object in self._get_related_objects_names(): q_objects.add(Q(**{f"{related_object}_count__gt": count}), Q.OR) - return self.annotate_related_objects_count().filter(q_objects) + return self._annotate_related_objects_count().filter(q_objects) + + def filter_all_related_gt(self, count): + exp =reduce(add, [F(f"{related_object}_count") for related_object in self._get_related_objects_names()]) + return self._annotate_related_objects_count()\ + .annotate(all_related_count=exp)\ + .filter(all_related_count__gt=count) From 7a0999a52032a90299e0f6fa36c90d6e63bdfd14 Mon Sep 17 00:00:00 2001 From: littlewolf Date: Fri, 27 Sep 2019 17:12:12 +0300 Subject: [PATCH 23/45] Add new filter --- apps/utils/querysets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/utils/querysets.py b/apps/utils/querysets.py index b94cb7b3..bf2816f2 100644 --- a/apps/utils/querysets.py +++ b/apps/utils/querysets.py @@ -49,6 +49,7 @@ class RelatedObjectsCountMixin(models.QuerySet): return self._annotate_related_objects_count().filter(q_objects) def filter_all_related_gt(self, count): + """Queryset filter by all related objects count""" exp =reduce(add, [F(f"{related_object}_count") for related_object in self._get_related_objects_names()]) return self._annotate_related_objects_count()\ .annotate(all_related_count=exp)\ From 03753f9059d673138e76b01a9a7f141c76874810 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Fri, 27 Sep 2019 17:16:40 +0300 Subject: [PATCH 24/45] News model, state --- .../migrations/0017_auto_20190927_1403.py | 22 ++++++++++++++ apps/news/models.py | 29 ++++++++++++++++--- apps/news/serializers.py | 6 ++-- 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 apps/news/migrations/0017_auto_20190927_1403.py diff --git a/apps/news/migrations/0017_auto_20190927_1403.py b/apps/news/migrations/0017_auto_20190927_1403.py new file mode 100644 index 00000000..1886dcec --- /dev/null +++ b/apps/news/migrations/0017_auto_20190927_1403.py @@ -0,0 +1,22 @@ +# Generated by Django 2.2.4 on 2019-09-27 14:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0016_news_template'), + ] + + operations = [ + migrations.RemoveField( + model_name='news', + name='is_publish', + ), + migrations.AddField( + model_name='news', + name='state', + field=models.PositiveSmallIntegerField(choices=[(0, 'Waiting'), (1, 'Hidden'), (2, 'Published'), (3, 'Published exclusive')], default=0, verbose_name='State'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index e0c5fbf4..9273a1bd 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -39,7 +39,7 @@ class NewsQuerySet(models.QuerySet): now = timezone.now() return self.filter(models.Q(models.Q(end__gte=now) | models.Q(end__isnull=True)), - is_publish=True, start__lte=now) + state__in=[self.model.PUBLISHED_STATES], start__lte=now) def with_related(self): """Return qs with related objects.""" @@ -49,7 +49,9 @@ class NewsQuerySet(models.QuerySet): class News(BaseAttributes, TranslatedFieldsMixin): """News model.""" - # template choices + STR_FIELD_NAME = 'title' + + # TEMPLATE CHOICES NEWSPAPER = 0 MAIN_PDF_ERB = 1 MAIN = 2 @@ -60,6 +62,21 @@ class News(BaseAttributes, TranslatedFieldsMixin): (MAIN, 'main'), ) + # STATE CHOICES + WAITING = 0 + HIDDEN = 1 + PUBLISHED = 2 + PUBLISHED_EXCLUSIVE = 3 + + PUBLISHED_STATES = [PUBLISHED, PUBLISHED_EXCLUSIVE] + + STATE_CHOICES = ( + (WAITING, _('Waiting')), + (HIDDEN, _('Hidden')), + (PUBLISHED, _('Published')), + (PUBLISHED_EXCLUSIVE, _('Published exclusive')), + ) + news_type = models.ForeignKey(NewsType, on_delete=models.PROTECT, verbose_name=_('news type')) title = TJSONField(blank=True, null=True, default=None, @@ -77,8 +94,8 @@ class News(BaseAttributes, TranslatedFieldsMixin): slug = models.SlugField(unique=True, max_length=50, verbose_name=_('News slug')) playlist = models.IntegerField(_('playlist')) - is_publish = models.BooleanField(default=False, - verbose_name=_('Publish status')) + state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES, + verbose_name=_('State')) author = models.CharField(max_length=255, blank=True, null=True, default=None,verbose_name=_('Author')) is_highlighted = models.BooleanField(default=False, @@ -109,6 +126,10 @@ class News(BaseAttributes, TranslatedFieldsMixin): def __str__(self): return f'news: {self.slug}' + @property + def is_publish(self): + return self.state in self.PUBLISHED_STATES + @property def web_url(self): return reverse('web:news:rud', kwargs={'slug': self.slug}) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 82f9980b..2677ce0f 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -50,6 +50,8 @@ class NewsDetailSerializer(NewsBaseSerializer): description_translated = TranslatedField() country = CountrySimpleSerializer(read_only=True) + state_display = serializers.CharField(source='get_state_display', + read_only=True) class Meta(NewsBaseSerializer.Meta): """Meta class.""" @@ -60,6 +62,8 @@ class NewsDetailSerializer(NewsBaseSerializer): 'end', 'playlist', 'is_publish', + 'state', + 'state_display', 'author', 'country', ) @@ -84,11 +88,9 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, news_type_id = serializers.PrimaryKeyRelatedField( source='news_type', write_only=True, queryset=models.NewsType.objects.all()) - country_id = serializers.PrimaryKeyRelatedField( source='country', write_only=True, queryset=location_models.Country.objects.all()) - template_display = serializers.CharField(source='get_template_display', read_only=True) From d25e4af4884235166a8a4a1fd5467fe01b2c5fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 27 Sep 2019 17:21:57 +0300 Subject: [PATCH 25/45] Created by --- apps/news/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 1778d410..320de8bd 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -50,7 +50,7 @@ class NewsDetailSerializer(NewsBaseSerializer): description_translated = TranslatedField() country = CountrySimpleSerializer(read_only=True) - author = UserSerializer(source='news_records_created') + author = UserSerializer(source='created_by') class Meta(NewsBaseSerializer.Meta): """Meta class.""" From 4f3c8042513dcbfe4bd691fb3cbd2e63d7c910bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 27 Sep 2019 17:34:13 +0300 Subject: [PATCH 26/45] Fix --- apps/news/migrations/0018_merge_20190927_1432.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 apps/news/migrations/0018_merge_20190927_1432.py diff --git a/apps/news/migrations/0018_merge_20190927_1432.py b/apps/news/migrations/0018_merge_20190927_1432.py new file mode 100644 index 00000000..c4654943 --- /dev/null +++ b/apps/news/migrations/0018_merge_20190927_1432.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-09-27 14:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0017_auto_20190927_1403'), + ('news', '0016_remove_news_author'), + ] + + operations = [ + ] From ac8356c7d18da921a21081da672264bff5440361 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 27 Sep 2019 19:03:30 +0300 Subject: [PATCH 27/45] Fix some test && routes && add migration --- apps/favorites/tests.py | 2 +- apps/news/migrations/0019_news_author.py | 18 ++++++++++++++++++ apps/news/models.py | 2 +- apps/news/tests.py | 2 +- apps/utils/tests.py | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 apps/news/migrations/0019_news_author.py diff --git a/apps/favorites/tests.py b/apps/favorites/tests.py index 208cf0db..99b01444 100644 --- a/apps/favorites/tests.py +++ b/apps/favorites/tests.py @@ -27,7 +27,7 @@ class BaseTestCase(APITestCase): news_type=self.test_news_type, description={"en-GB": "Description test news"}, playlist=1, start="2020-12-03 12:00:00", end="2020-12-13 12:00:00", - is_publish=True) + state=News.PUBLISHED, slug='test-news') self.test_content_type = ContentType.objects.get(app_label="news", model="news") diff --git a/apps/news/migrations/0019_news_author.py b/apps/news/migrations/0019_news_author.py new file mode 100644 index 00000000..41985255 --- /dev/null +++ b/apps/news/migrations/0019_news_author.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-09-27 15:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0018_merge_20190927_1432'), + ] + + operations = [ + migrations.AddField( + model_name='news', + name='author', + field=models.CharField(blank=True, default=None, max_length=255, null=True, verbose_name='Author'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 2c6229be..e6967d00 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -39,7 +39,7 @@ class NewsQuerySet(models.QuerySet): now = timezone.now() return self.filter(models.Q(models.Q(end__gte=now) | models.Q(end__isnull=True)), - state__in=[self.model.PUBLISHED_STATES], start__lte=now) + state__in=self.model.PUBLISHED_STATES, start__lte=now) def with_related(self): """Return qs with related objects.""" diff --git a/apps/news/tests.py b/apps/news/tests.py index 7d6724d7..2e24ac45 100644 --- a/apps/news/tests.py +++ b/apps/news/tests.py @@ -26,7 +26,7 @@ class BaseTestCase(APITestCase): news_type=self.test_news_type, description={"en-GB": "Description test news"}, playlist=1, start=datetime.now() + timedelta(hours=-2), end=datetime.now() + timedelta(hours=2), - is_publish=True, slug='test-news-slug',) + state=News.PUBLISHED, slug='test-news-slug',) class NewsTestCase(BaseTestCase): diff --git a/apps/utils/tests.py b/apps/utils/tests.py index 4adc2f1e..10af2a92 100644 --- a/apps/utils/tests.py +++ b/apps/utils/tests.py @@ -54,9 +54,9 @@ class TranslateFieldTests(BaseTestCase): playlist=1, start=datetime.now(pytz.utc) + timedelta(hours=-13), end=datetime.now(pytz.utc) + timedelta(hours=13), - is_publish=True, news_type=self.news_type, slug='test', + state=News.PUBLISHED, ) def test_model_field(self): From d71ed0c1de634dee9335b28efde89f41047e5ff9 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 27 Sep 2019 19:44:00 +0300 Subject: [PATCH 28/45] Fix lot/lat x/y mapping && address serialization --- apps/establishment/serializers/common.py | 11 +++++++++-- apps/establishment/views/web.py | 2 +- apps/location/serializers/common.py | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 69a029e2..0e33921c 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -5,7 +5,7 @@ from comment import models as comment_models from comment.serializers import common as comment_serializers from establishment import models from favorites.models import Favorites -from location.serializers import AddressSimpleSerializer +from location.serializers import AddressSimpleSerializer, AddressSerializer from main.models import MetaDataContent from main.serializers import MetaDataContentSerializer, AwardSerializer, CurrencySerializer from review import models as review_models @@ -146,7 +146,7 @@ class EstablishmentBaseSerializer(serializers.ModelSerializer): preview_image = serializers.URLField(source='preview_image_url') slug = serializers.SlugField(allow_blank=False, required=True, max_length=50) - address = AddressSimpleSerializer() + address = AddressSerializer() tags = MetaDataContentSerializer(many=True) class Meta: @@ -180,6 +180,13 @@ class EstablishmentListSerializer(EstablishmentBaseSerializer): 'in_favorites', ] +class EstablishmentAllListSerializer(EstablishmentListSerializer): + """ Serailizer for api/*/establishments """ + address = AddressSimpleSerializer() + + class Meta(EstablishmentListSerializer.Meta): + pass + class EstablishmentDetailSerializer(EstablishmentListSerializer): """Serializer for Establishment model.""" diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 4ece55c0..f4558b71 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -28,7 +28,7 @@ class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView): """Resource for getting a list of establishments.""" filter_class = filters.EstablishmentFilter - serializer_class = serializers.EstablishmentListSerializer + serializer_class = serializers.EstablishmentAllListSerializer def get_queryset(self): """Overridden method 'get_queryset'.""" diff --git a/apps/location/serializers/common.py b/apps/location/serializers/common.py index 1dee92ed..37d782de 100644 --- a/apps/location/serializers/common.py +++ b/apps/location/serializers/common.py @@ -104,7 +104,7 @@ class AddressSerializer(serializers.ModelSerializer): 'number', 'postal_code', 'geo_lon', - 'geo_lat' + 'geo_lat', ] def validate(self, attrs): From 1957ab6300bed2c71d3e6d7f0fb4135ee0eaf57e Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 27 Sep 2019 20:54:55 +0300 Subject: [PATCH 29/45] Pagination for ES results --- apps/search_indexes/views.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index 72cab583..0e73ed25 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -4,7 +4,8 @@ from django_elasticsearch_dsl_drf import constants from django_elasticsearch_dsl_drf.filter_backends import (FilteringFilterBackend, GeoSpatialFilteringFilterBackend) from django_elasticsearch_dsl_drf.viewsets import BaseDocumentViewSet -from django_elasticsearch_dsl_drf.pagination import PageNumberPagination + +from pagination import ProjectPageNumberPagination from search_indexes import serializers, filters from search_indexes.documents import EstablishmentDocument, NewsDocument @@ -14,7 +15,7 @@ class NewsDocumentViewSet(BaseDocumentViewSet): document = NewsDocument lookup_field = 'slug' - pagination_class = PageNumberPagination + pagination_class = ProjectPageNumberPagination permission_classes = (permissions.AllowAny,) serializer_class = serializers.NewsDocumentSerializer ordering = ('id',) @@ -40,7 +41,7 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): document = EstablishmentDocument lookup_field = 'slug' - pagination_class = PageNumberPagination + pagination_class = ProjectPageNumberPagination permission_classes = (permissions.AllowAny,) serializer_class = serializers.EstablishmentDocumentSerializer ordering = ('id',) From 4d113a691685b205074ebe765d897c6b26cfb56d Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 27 Sep 2019 22:06:46 +0300 Subject: [PATCH 30/45] Fix all test --- apps/establishment/serializers/back.py | 2 +- apps/establishment/tests.py | 7 +++++++ apps/utils/tests.py | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 788fa1e1..5a15aafc 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -22,7 +22,7 @@ class EstablishmentListCreateSerializer(EstablishmentBaseSerializer): emails = ContactEmailsSerializer(read_only=True, many=True, ) socials = SocialNetworkRelatedSerializers(read_only=True, many=True, ) slug = serializers.SlugField(required=True, allow_blank=False, max_length=50) - type = EstablishmentTypeSerializer(source='establishment_type') + type = EstablishmentTypeSerializer(source='establishment_type', read_only=True) class Meta: model = models.Establishment diff --git a/apps/establishment/tests.py b/apps/establishment/tests.py index 16d224b8..39e28861 100644 --- a/apps/establishment/tests.py +++ b/apps/establishment/tests.py @@ -6,6 +6,7 @@ from http.cookies import SimpleCookie from main.models import Currency from establishment.models import Establishment, EstablishmentType, Menu # Create your tests here. +from translation.models import Language class BaseTestCase(APITestCase): @@ -25,6 +26,12 @@ class BaseTestCase(APITestCase): self.establishment_type = EstablishmentType.objects.create(name="Test establishment type") + # Create lang object + Language.objects.create( + title='English', + locale='en-GB' + ) + class EstablishmentBTests(BaseTestCase): def test_establishment_CRUD(self): diff --git a/apps/utils/tests.py b/apps/utils/tests.py index 10af2a92..0eaf343d 100644 --- a/apps/utils/tests.py +++ b/apps/utils/tests.py @@ -95,6 +95,7 @@ class BaseAttributeTests(BaseTestCase): response_data = response.json() self.assertIn("id", response_data) + self.assertIsInstance(response_data['id'], int) employee = Employee.objects.get(id=response_data['id']) @@ -118,7 +119,7 @@ class BaseAttributeTests(BaseTestCase): 'name': 'Test new name' } - response = self.client.patch('/api/back/establishments/employees/1/', data=update_data) + response = self.client.patch(f'/api/back/establishments/employees/{employee.pk}/', data=update_data) self.assertEqual(response.status_code, status.HTTP_200_OK) employee.refresh_from_db() From 4388fb73468d57473c7c227dc89d147c435bcba7 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 25 Sep 2019 22:00:05 +0300 Subject: [PATCH 31/45] latest award for establishment --- apps/establishment/models.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index f677737d..9c402a76 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -281,7 +281,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): slug = models.SlugField(unique=True, max_length=50, null=True, verbose_name=_('Establishment slug'), editable=True) - awards = generic.GenericRelation(to='main.Award') + establishment_awards = generic.GenericRelation(to='main.Award') tags = generic.GenericRelation(to='main.MetaDataContent') reviews = generic.GenericRelation(to='review.Review') comments = generic.GenericRelation(to='comment.Comment') @@ -312,6 +312,19 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): country = self.address.city.country return country.low_price, country.high_price + @property + def awards(self): + latest_employee_awards = Employee.objects.filter( + establishments=self + ).annotate( + latest_employee_award=Max('awards__vintage_year') + ).filter( + awards__vintage_year=F('awards__vintage_year') + ).get().awards + latest_establishment_award = self.establishment_awards.latest(field_name='vintage_year').get() + awards = [award for award in latest_employee_awards + [latest_establishment_award] if award is not None] + return [awards.sort(key=lambda award: award.vintage_year)[0]] + # todo: make via prefetch # @property # def subtypes(self): From 98b0a9af5d5532c5bd869e40b9521ae71f227e2b Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 26 Sep 2019 01:26:52 +0300 Subject: [PATCH 32/45] latest award && test fix --- apps/establishment/models.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 9c402a76..bebacd18 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -16,6 +16,8 @@ from phonenumber_field.modelfields import PhoneNumberField from collection.models import Collection from main.models import MetaDataContent from location.models import Address +from collection.models import Collection +from main.models import Award from review.models import Review from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, TranslatedFieldsMixin, BaseAttributes) @@ -281,7 +283,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): slug = models.SlugField(unique=True, max_length=50, null=True, verbose_name=_('Establishment slug'), editable=True) - establishment_awards = generic.GenericRelation(to='main.Award') + establishment_awards = generic.GenericRelation(to='main.Award', related_query_name='establishment') tags = generic.GenericRelation(to='main.MetaDataContent') reviews = generic.GenericRelation(to='review.Review') comments = generic.GenericRelation(to='comment.Comment') @@ -314,16 +316,8 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): @property def awards(self): - latest_employee_awards = Employee.objects.filter( - establishments=self - ).annotate( - latest_employee_award=Max('awards__vintage_year') - ).filter( - awards__vintage_year=F('awards__vintage_year') - ).get().awards - latest_establishment_award = self.establishment_awards.latest(field_name='vintage_year').get() - awards = [award for award in latest_employee_awards + [latest_establishment_award] if award is not None] - return [awards.sort(key=lambda award: award.vintage_year)[0]] + return [Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)).latest( + field_name='vintage_year')] # todo: make via prefetch # @property @@ -422,8 +416,8 @@ class Employee(BaseAttributes): verbose_name=_('User')) name = models.CharField(max_length=255, verbose_name=_('Last name')) establishments = models.ManyToManyField(Establishment, related_name='employees', - through=EstablishmentEmployee) - awards = generic.GenericRelation(to='main.Award') + through=EstablishmentEmployee,) + awards = generic.GenericRelation(to='main.Award', related_query_name='employees') tags = generic.GenericRelation(to='main.MetaDataContent') class Meta: From 051ddb617d1c056214081a9768ce43b1c3b1a776 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 26 Sep 2019 18:20:06 +0300 Subject: [PATCH 33/45] fix conflicts after rebase --- apps/establishment/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index bebacd18..fcc6abe6 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -8,7 +8,7 @@ from django.contrib.gis.geos import Point from django.contrib.gis.measure import Distance as DistanceMeasure from django.core.exceptions import ValidationError from django.db import models -from django.db.models import When, Case, F, ExpressionWrapper, Subquery +from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField From 9aafe0d69d04e4ec88871c6a84666681542b746a Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 27 Sep 2019 16:00:08 +0300 Subject: [PATCH 34/45] new establishment attribute --- apps/establishment/models.py | 12 ++++++------ apps/main/serializers.py | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index fcc6abe6..819cd172 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -191,6 +191,11 @@ class EstablishmentQuerySet(models.QuerySet): return self.filter(id__in=subquery_filter_by_distance) \ .order_by('-reviews__published_at') + def the_most_recent_award(self): + """ Returns the most recent award for carousel """ + return Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)).latest( + field_name='vintage_year') + def prefetch_actual_employees(self): """Prefetch actual employees.""" return self.prefetch_related( @@ -283,7 +288,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): slug = models.SlugField(unique=True, max_length=50, null=True, verbose_name=_('Establishment slug'), editable=True) - establishment_awards = generic.GenericRelation(to='main.Award', related_query_name='establishment') + awards = generic.GenericRelation(to='main.Award', related_query_name='establishment') tags = generic.GenericRelation(to='main.MetaDataContent') reviews = generic.GenericRelation(to='review.Review') comments = generic.GenericRelation(to='comment.Comment') @@ -314,11 +319,6 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): country = self.address.city.country return country.low_price, country.high_price - @property - def awards(self): - return [Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)).latest( - field_name='vintage_year')] - # todo: make via prefetch # @property # def subtypes(self): diff --git a/apps/main/serializers.py b/apps/main/serializers.py index c981f0ee..e16cdd9b 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -141,6 +141,7 @@ class CarouselListSerializer(serializers.ModelSerializer): image = serializers.URLField(source='image_url') awards = AwardBaseSerializer(many=True) vintage_year = serializers.IntegerField() + last_award = serializers.SerializerMethodField() class Meta: """Meta class.""" @@ -154,8 +155,11 @@ class CarouselListSerializer(serializers.ModelSerializer): 'public_mark', 'image', 'vintage_year', + 'last_award', ] + def get_last_award(self, obj): + return obj.the_most_recent_award() class PageSerializer(serializers.ModelSerializer): page_name = serializers.CharField() From d42bf43d1963355f14a35363d4ef4ef719e7fa60 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 27 Sep 2019 22:56:44 +0300 Subject: [PATCH 35/45] Latest award for carousel --- apps/establishment/models.py | 12 +++++++----- apps/main/serializers.py | 6 +++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 819cd172..23bc3c8f 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -191,11 +191,6 @@ class EstablishmentQuerySet(models.QuerySet): return self.filter(id__in=subquery_filter_by_distance) \ .order_by('-reviews__published_at') - def the_most_recent_award(self): - """ Returns the most recent award for carousel """ - return Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)).latest( - field_name='vintage_year') - def prefetch_actual_employees(self): """Prefetch actual employees.""" return self.prefetch_related( @@ -363,6 +358,13 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): """ return self.address.coordinates + @property + def the_most_recent_award(self): + awards = Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)) + if awards: + return awards.latest(field_name='vintage_year') + return {} + class Position(BaseAttributes, TranslatedFieldsMixin): """Position model.""" diff --git a/apps/main/serializers.py b/apps/main/serializers.py index e16cdd9b..a0b49525 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -3,6 +3,7 @@ from rest_framework import serializers from advertisement.serializers.web import AdvertisementSerializer from location.serializers import CountrySerializer from main import models +from establishment.models import Establishment from utils.serializers import TranslatedField @@ -159,7 +160,10 @@ class CarouselListSerializer(serializers.ModelSerializer): ] def get_last_award(self, obj): - return obj.the_most_recent_award() + if isinstance(obj.content_object, Establishment): + award = obj.content_object.the_most_recent_award + return AwardBaseSerializer().to_representation(award) if award else None + return None class PageSerializer(serializers.ModelSerializer): page_name = serializers.CharField() From 78e1e448d9575b3463a6f95fa65de1e03bbfba19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 30 Sep 2019 10:09:46 +0300 Subject: [PATCH 36/45] News search --- apps/search_indexes/urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/search_indexes/urls.py b/apps/search_indexes/urls.py index 8e914719..a566af1f 100644 --- a/apps/search_indexes/urls.py +++ b/apps/search_indexes/urls.py @@ -7,5 +7,6 @@ router = routers.SimpleRouter() # router.register(r'news', views.NewsDocumentViewSet, basename='news') # temporarily disabled router.register(r'establishments', views.EstablishmentDocumentViewSet, basename='establishment') router.register(r'mobile/establishments', views.EstablishmentDocumentViewSet, basename='establishment-mobile') +router.register(r'news', views.NewsDocumentViewSet, basename='news') urlpatterns = router.urls From 5ef33c0d1920c913ea422691b514d35d4eedbe02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 30 Sep 2019 10:19:09 +0300 Subject: [PATCH 37/45] News search url --- apps/search_indexes/urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/search_indexes/urls.py b/apps/search_indexes/urls.py index a566af1f..549e569d 100644 --- a/apps/search_indexes/urls.py +++ b/apps/search_indexes/urls.py @@ -10,3 +10,4 @@ router.register(r'mobile/establishments', views.EstablishmentDocumentViewSet, ba router.register(r'news', views.NewsDocumentViewSet, basename='news') urlpatterns = router.urls + From 310928d95cd75ff3b760dd2717920937b768604d Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 30 Sep 2019 12:46:58 +0300 Subject: [PATCH 38/45] Fix import package issue --- apps/search_indexes/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index 0e73ed25..a69caf1f 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -5,7 +5,7 @@ from django_elasticsearch_dsl_drf.filter_backends import (FilteringFilterBackend GeoSpatialFilteringFilterBackend) from django_elasticsearch_dsl_drf.viewsets import BaseDocumentViewSet -from pagination import ProjectPageNumberPagination +from utils.pagination import ProjectPageNumberPagination from search_indexes import serializers, filters from search_indexes.documents import EstablishmentDocument, NewsDocument From 019ac97d19a7691ce93cc65d21a350ceb825a38c Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 30 Sep 2019 13:50:17 +0300 Subject: [PATCH 39/45] Add vintage year to detail --- apps/establishment/models.py | 7 +++++++ apps/establishment/serializers/common.py | 2 ++ 2 files changed, 9 insertions(+) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 23bc3c8f..ba34098d 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -332,6 +332,13 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): raise ValidationError('Establishment type of subtype does not match') self.establishment_subtypes.add(establishment_subtype) + + @property + def vintage_year(self): + review_qs = self.reviews.by_status(Review.READY) + if review_qs.exists(): + return review_qs.last().vintage + @property def best_price_menu(self): return 150 diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 69a029e2..fa7d4b63 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -199,6 +199,7 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer): best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) + vintage_year = serializers.ReadOnlyField() class Meta(EstablishmentListSerializer.Meta): """Meta class.""" @@ -222,6 +223,7 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer): 'best_price_menu', 'best_price_carte', 'transportation', + 'vintage_year', ] # def get_in_favorites(self, obj): From b1e09d806a9bfa23e52f51074813eff2c315b78d Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 30 Sep 2019 14:00:34 +0300 Subject: [PATCH 40/45] Add ordering to timetable object --- apps/timetable/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/timetable/models.py b/apps/timetable/models.py index e1e7fae7..53670d02 100644 --- a/apps/timetable/models.py +++ b/apps/timetable/models.py @@ -36,3 +36,4 @@ class Timetable(ProjectBaseMixin): """Meta class.""" verbose_name = _('Timetable') verbose_name_plural = _('Timetables') + ordering = ['weekday'] From beb031d0b67a4b2b6425cf969496da7aa74988d4 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 30 Sep 2019 14:05:38 +0300 Subject: [PATCH 41/45] Add slug to carousel serializer --- apps/main/models.py | 5 +++++ apps/main/serializers.py | 1 + 2 files changed, 6 insertions(+) diff --git a/apps/main/models.py b/apps/main/models.py index 47ac90be..6d4305d4 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -337,6 +337,11 @@ class Carousel(models.Model): if hasattr(self.content_object, 'image_url'): return self.content_object.image_url + @property + def slug(self): + if hasattr(self.content_object, 'slug'): + return self.content_object.slug + @property def model_name(self): return self.content_object.__class__.__name__ diff --git a/apps/main/serializers.py b/apps/main/serializers.py index a0b49525..6395e25f 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -157,6 +157,7 @@ class CarouselListSerializer(serializers.ModelSerializer): 'image', 'vintage_year', 'last_award', + 'slug', ] def get_last_award(self, obj): From 33478580aadbe0a0c2ebefd27fe63e1f90fbaf6f Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 30 Sep 2019 16:40:46 +0300 Subject: [PATCH 42/45] Review fixes --- apps/establishment/models.py | 16 ++++++---------- apps/establishment/serializers/common.py | 2 +- apps/main/models.py | 11 ++++++++--- apps/main/serializers.py | 7 +------ 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index ba34098d..589c2e22 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -14,10 +14,8 @@ from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField from collection.models import Collection -from main.models import MetaDataContent +from main.models import Award, MetaDataContent from location.models import Address -from collection.models import Collection -from main.models import Award from review.models import Review from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, TranslatedFieldsMixin, BaseAttributes) @@ -335,9 +333,9 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): @property def vintage_year(self): - review_qs = self.reviews.by_status(Review.READY) - if review_qs.exists(): - return review_qs.last().vintage + last_review = self.reviews.by_status(Review.READY).last() + if last_review: + return last_review.vintage @property def best_price_menu(self): @@ -367,10 +365,8 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): @property def the_most_recent_award(self): - awards = Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)) - if awards: - return awards.latest(field_name='vintage_year') - return {} + return Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)).latest( + field_name='vintage_year') class Position(BaseAttributes, TranslatedFieldsMixin): diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index fa7d4b63..5e74b178 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -198,8 +198,8 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer): menu = MenuSerializers(source='menu_set', many=True, read_only=True) best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) - vintage_year = serializers.ReadOnlyField() + class Meta(EstablishmentListSerializer.Meta): """Meta class.""" diff --git a/apps/main/models.py b/apps/main/models.py index 6d4305d4..2270cbf9 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -318,9 +318,9 @@ class Carousel(models.Model): @property def vintage_year(self): if hasattr(self.content_object, 'reviews'): - review_qs = self.content_object.reviews.by_status(Review.READY) - if review_qs.exists(): - return review_qs.last().vintage + last_review = self.content_object.reviews.by_status(Review.READY).last() + if last_review: + return last_review.vintage @property def toque_number(self): @@ -342,6 +342,11 @@ class Carousel(models.Model): if hasattr(self.content_object, 'slug'): return self.content_object.slug + @property + def the_most_recent_award(self): + if hasattr(self.content_object, 'the_most_recent_award'): + return self.content_object.the_most_recent_award + @property def model_name(self): return self.content_object.__class__.__name__ diff --git a/apps/main/serializers.py b/apps/main/serializers.py index 6395e25f..03ea73e6 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -142,7 +142,7 @@ class CarouselListSerializer(serializers.ModelSerializer): image = serializers.URLField(source='image_url') awards = AwardBaseSerializer(many=True) vintage_year = serializers.IntegerField() - last_award = serializers.SerializerMethodField() + last_award = AwardBaseSerializer(source='the_most_recent_award', allow_null=True) class Meta: """Meta class.""" @@ -160,11 +160,6 @@ class CarouselListSerializer(serializers.ModelSerializer): 'slug', ] - def get_last_award(self, obj): - if isinstance(obj.content_object, Establishment): - award = obj.content_object.the_most_recent_award - return AwardBaseSerializer().to_representation(award) if award else None - return None class PageSerializer(serializers.ModelSerializer): page_name = serializers.CharField() From 693d895aa4d9afe498ddb71bccb6cf3e03439113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Tue, 1 Oct 2019 11:52:46 +0300 Subject: [PATCH 43/45] Fix tests --- project/settings/local.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/project/settings/local.py b/project/settings/local.py index 503ad191..a644e9b8 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -1,5 +1,6 @@ """Local settings.""" from .base import * +import sys ALLOWED_HOSTS = ['*', ] @@ -65,5 +66,9 @@ ELASTICSEARCH_DSL = { ELASTICSEARCH_INDEX_NAMES = { # 'search_indexes.documents.news': 'local_news', - 'search_indexes.documents.establishment': 'local_establishment', -} \ No newline at end of file + 'search_indexes.documents.establishment': 'local_establishment', +} + +TESTING = sys.argv[1:2] == ['test'] +if TESTING: + ELASTICSEARCH_INDEX_NAMES = {} From 051957c7cf37268c128228ffa79cbe6ee73077d8 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Tue, 1 Oct 2019 13:03:59 +0300 Subject: [PATCH 44/45] Refactor JSON Validation --- apps/establishment/serializers/back.py | 5 +++-- apps/establishment/serializers/common.py | 27 ++++++------------------ apps/news/serializers.py | 10 +++++---- apps/news/views.py | 3 +++ apps/utils/serializers.py | 24 +++++++++++++-------- 5 files changed, 34 insertions(+), 35 deletions(-) diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 5a15aafc..d0c70b2f 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -8,7 +8,6 @@ from establishment.serializers import ( from utils.decorators import with_base_attributes from main.models import Currency -from utils.serializers import TJSONSerializer class EstablishmentListCreateSerializer(EstablishmentBaseSerializer): @@ -89,13 +88,15 @@ class SocialNetworkSerializers(serializers.ModelSerializer): class PlatesSerializers(PlateSerializer): """Social network serializers.""" - name = TJSONSerializer + currency_id = serializers.PrimaryKeyRelatedField( source='currency', queryset=Currency.objects.all(), write_only=True ) class Meta: + """Meta class.""" + model = models.Plate fields = PlateSerializer.Meta.fields + [ 'name', diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index dc718d4f..e3857bd7 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -1,6 +1,6 @@ """Establishment serializers.""" -from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers from comment import models as comment_models from comment.serializers import common as comment_serializers from establishment import models @@ -11,8 +11,7 @@ from main.serializers import MetaDataContentSerializer, AwardSerializer, Currenc from review import models as review_models from timetable.serialziers import ScheduleRUDSerializer from utils import exceptions as utils_exceptions -from utils.serializers import TranslatedField -from utils.serializers import TJSONSerializer +from utils.serializers import TranslatedField, ProjectModelSerializer class ContactPhonesSerializer(serializers.ModelSerializer): @@ -44,9 +43,9 @@ class SocialNetworkRelatedSerializers(serializers.ModelSerializer): ] -class PlateSerializer(serializers.ModelSerializer): +class PlateSerializer(ProjectModelSerializer): - name_translated = serializers.CharField(allow_null=True, read_only=True) + name_translated = TranslatedField() currency = CurrencySerializer(read_only=True) class Meta: @@ -59,9 +58,8 @@ class PlateSerializer(serializers.ModelSerializer): ] -class MenuSerializers(serializers.ModelSerializer): +class MenuSerializers(ProjectModelSerializer): plates = PlateSerializer(read_only=True, many=True, source='plate_set') - category = TJSONSerializer() category_translated = serializers.CharField(read_only=True) class Meta: @@ -75,9 +73,8 @@ class MenuSerializers(serializers.ModelSerializer): ] -class MenuRUDSerializers(serializers.ModelSerializer, ): +class MenuRUDSerializers(ProjectModelSerializer): plates = PlateSerializer(read_only=True, many=True, source='plate_set') - category = TJSONSerializer() class Meta: model = models.Menu @@ -141,7 +138,7 @@ class EstablishmentEmployeeSerializer(serializers.ModelSerializer): fields = ('id', 'name', 'position_translated', 'awards', 'priority') -class EstablishmentBaseSerializer(serializers.ModelSerializer): +class EstablishmentBaseSerializer(ProjectModelSerializer): """Base serializer for Establishment model.""" preview_image = serializers.URLField(source='preview_image_url') @@ -233,16 +230,6 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer): 'vintage_year', ] - # def get_in_favorites(self, obj): - # """Get in_favorites status flag""" - # user = self.context.get('request').user - # if user.is_authenticated: - # return obj.id in user.favorites.by_content_type(app_label='establishment', - # model='establishment')\ - # .values_list('object_id', flat=True) - # else: - # return False - class EstablishmentCommentCreateSerializer(comment_serializers.CommentSerializer): """Create comment serializer""" diff --git a/apps/news/serializers.py b/apps/news/serializers.py index b144fdc5..248a03b7 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -1,11 +1,12 @@ """News app common serializers.""" from rest_framework import serializers +from account.serializers.common import UserSerializer from location import models as location_models from location.serializers import CountrySimpleSerializer from main.serializers import MetaDataContentSerializer from news import models -from utils.serializers import TranslatedField -from account.serializers.common import UserSerializer +from utils.serializers import TranslatedField, ProjectModelSerializer + class NewsTypeSerializer(serializers.ModelSerializer): """News type serializer.""" @@ -17,7 +18,7 @@ class NewsTypeSerializer(serializers.ModelSerializer): fields = ('id', 'name') -class NewsBaseSerializer(serializers.ModelSerializer): +class NewsBaseSerializer(ProjectModelSerializer): """Base serializer for News model.""" # read only fields @@ -50,7 +51,8 @@ class NewsDetailSerializer(NewsBaseSerializer): description_translated = TranslatedField() country = CountrySimpleSerializer(read_only=True) - author = UserSerializer(source='created_by') + # todo: check the data redundancy + author = UserSerializer(source='created_by', read_only=True) state_display = serializers.CharField(source='get_state_display', read_only=True) diff --git a/apps/news/views.py b/apps/news/views.py index 74abe33f..c5e69ab0 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -20,11 +20,13 @@ class NewsMixinView: class NewsListView(NewsMixinView, generics.ListAPIView): """News list view.""" + filter_class = filters.NewsListFilterSet class NewsDetailView(NewsMixinView, generics.RetrieveAPIView): """News detail view.""" + lookup_field = 'slug' serializer_class = serializers.NewsDetailSerializer @@ -54,6 +56,7 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView, create_serializers_class = serializers.NewsBackOfficeDetailSerializer def get_serializer_class(self): + """Override serializer class.""" if self.request.method == 'POST': return self.create_serializers_class return super().get_serializer_class() diff --git a/apps/utils/serializers.py b/apps/utils/serializers.py index d30a046c..2b2282d1 100644 --- a/apps/utils/serializers.py +++ b/apps/utils/serializers.py @@ -1,7 +1,7 @@ """Utils app serializer.""" -from rest_framework import serializers -from utils.models import PlatformMixin from django.core import exceptions +from rest_framework import serializers +from utils import models from translation.models import Language @@ -11,8 +11,8 @@ class EmptySerializer(serializers.Serializer): class SourceSerializerMixin(serializers.Serializer): """Base authorization serializer mixin""" - source = serializers.ChoiceField(choices=PlatformMixin.SOURCES, - default=PlatformMixin.WEB, + source = serializers.ChoiceField(choices=models.PlatformMixin.SOURCES, + default=models.PlatformMixin.WEB, write_only=True) @@ -25,18 +25,16 @@ class TranslatedField(serializers.CharField): read_only=read_only, **kwargs) +# todo: view validation in more detail def validate_tjson(value): - if not isinstance(value, dict): raise exceptions.ValidationError( 'invalid_json', code='invalid_json', params={'value': value}, ) - lang_count = Language.objects.filter(locale__in=value.keys()).count() - - if lang_count == 0: + if lang_count != len(value.keys()): raise exceptions.ValidationError( 'invalid_translated_keys', code='invalid_translated_keys', @@ -44,5 +42,13 @@ def validate_tjson(value): ) -class TJSONSerializer(serializers.JSONField): +class TJSONField(serializers.JSONField): + """Custom serializer's JSONField for model's TJSONField.""" + validators = [validate_tjson] + + +class ProjectModelSerializer(serializers.ModelSerializer): + """Overrided ModelSerializer.""" + + serializers.ModelSerializer.serializer_field_mapping[models.TJSONField] = TJSONField From 9f280857effb0264ba5af6ba83b1c84fe8c7b404 Mon Sep 17 00:00:00 2001 From: Dmitriy Kuzmenko Date: Tue, 1 Oct 2019 17:42:37 +0300 Subject: [PATCH 45/45] change docker-compose for working celery --- apps/collection/tests.py | 14 +++++++------- docker-compose.yml | 12 +----------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/apps/collection/tests.py b/apps/collection/tests.py index ea13fff9..72b40c37 100644 --- a/apps/collection/tests.py +++ b/apps/collection/tests.py @@ -1,15 +1,15 @@ -import json, pytz +import json +import pytz from datetime import datetime -from rest_framework.test import APITestCase -from account.models import User -from rest_framework import status from http.cookies import SimpleCookie -from collection.models import Collection, Guide -from location.models import Country +from rest_framework import status +from rest_framework.test import APITestCase +from account.models import User +from collection.models import Collection, Guide from establishment.models import Establishment, EstablishmentType -# Create your tests here. +from location.models import Country class BaseTestCase(APITestCase): diff --git a/docker-compose.yml b/docker-compose.yml index 3fb05f98..7c4e49d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,8 +12,6 @@ services: - POSTGRES_DB=postgres ports: - "5436:5432" - networks: - - db-net volumes: - gm-db:/var/lib/postgresql/data/ elasticsearch: @@ -28,8 +26,7 @@ services: - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - discovery.type=single-node - xpack.security.enabled=false - networks: - - app-net + # RabbitMQ rabbitmq: image: rabbitmq:latest @@ -83,19 +80,12 @@ services: - worker - worker_beat - elasticsearch - networks: - - app-net - - db-net volumes: - .:/code - gm-media:/media-data ports: - "8000:8000" -networks: - app-net: - db-net: - volumes: gm-db: name: gm-db