From 4388fb73468d57473c7c227dc89d147c435bcba7 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 25 Sep 2019 22:00:05 +0300 Subject: [PATCH 01/14] 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 02/14] 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 03/14] 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 04/14] 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 05/14] 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 019ac97d19a7691ce93cc65d21a350ceb825a38c Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 30 Sep 2019 13:50:17 +0300 Subject: [PATCH 06/14] 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 07/14] 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 08/14] 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 09/14] 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 10/14] 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 11/14] 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 f034377405feb3868f6311b9fccc931c4254f3d8 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Tue, 1 Oct 2019 15:11:14 +0300 Subject: [PATCH 12/14] fix establishment serializer --- apps/establishment/serializers/common.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index e3857bd7..f3ae3dc0 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -145,6 +145,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): slug = serializers.SlugField(allow_blank=False, required=True, max_length=50) address = AddressSerializer() tags = MetaDataContentSerializer(many=True) + in_favorites = serializers.BooleanField(allow_null=True) class Meta: """Meta class.""" @@ -168,14 +169,6 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): class EstablishmentListSerializer(EstablishmentBaseSerializer): """Serializer for Establishment model.""" - in_favorites = serializers.BooleanField(allow_null=True) - - class Meta(EstablishmentBaseSerializer.Meta): - """Meta class.""" - - fields = EstablishmentBaseSerializer.Meta.fields + [ - 'in_favorites', - ] class EstablishmentAllListSerializer(EstablishmentListSerializer): """ Serailizer for api/*/establishments """ From 9f280857effb0264ba5af6ba83b1c84fe8c7b404 Mon Sep 17 00:00:00 2001 From: Dmitriy Kuzmenko Date: Tue, 1 Oct 2019 17:42:37 +0300 Subject: [PATCH 13/14] 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 From e42f4d061cf991ce02914aa088456179758764d8 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Tue, 1 Oct 2019 18:07:04 +0300 Subject: [PATCH 14/14] refactor address --- apps/collection/views/common.py | 4 +- apps/establishment/serializers/common.py | 22 ++---- apps/establishment/views/web.py | 10 ++- apps/favorites/views.py | 7 +- apps/location/models.py | 5 +- apps/location/serializers/back.py | 5 +- apps/location/serializers/common.py | 99 +++++++++++++----------- apps/location/views/back.py | 5 +- apps/location/views/common.py | 6 +- 9 files changed, 79 insertions(+), 84 deletions(-) diff --git a/apps/collection/views/common.py b/apps/collection/views/common.py index 148c5fab..fd2a4584 100644 --- a/apps/collection/views/common.py +++ b/apps/collection/views/common.py @@ -4,7 +4,7 @@ from rest_framework import permissions from collection import models from utils.pagination import ProjectPageNumberPagination from django.shortcuts import get_object_or_404 -from establishment.serializers import EstablishmentListSerializer +from establishment.serializers import EstablishmentBaseSerializer from collection.serializers import common as serializers @@ -56,7 +56,7 @@ class CollectionEstablishmentListView(CollectionListView): """Retrieve list of establishment for collection.""" permission_classes = (permissions.AllowAny,) pagination_class = ProjectPageNumberPagination - serializer_class = EstablishmentListSerializer + serializer_class = EstablishmentBaseSerializer lookup_field = 'slug' def get_queryset(self): diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index f3ae3dc0..f09c8200 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, AddressSerializer +from location.serializers import AddressBaseSerializer from main.models import MetaDataContent from main.serializers import MetaDataContentSerializer, AwardSerializer, CurrencySerializer from review import models as review_models @@ -143,7 +143,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): preview_image = serializers.URLField(source='preview_image_url') slug = serializers.SlugField(allow_blank=False, required=True, max_length=50) - address = AddressSerializer() + address = AddressBaseSerializer() tags = MetaDataContentSerializer(many=True) in_favorites = serializers.BooleanField(allow_null=True) @@ -166,19 +166,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): ] -class EstablishmentListSerializer(EstablishmentBaseSerializer): - """Serializer for Establishment model.""" - - -class EstablishmentAllListSerializer(EstablishmentListSerializer): - """ Serailizer for api/*/establishments """ - address = AddressSimpleSerializer() - - class Meta(EstablishmentListSerializer.Meta): - pass - - -class EstablishmentDetailSerializer(EstablishmentListSerializer): +class EstablishmentDetailSerializer(EstablishmentBaseSerializer): """Serializer for Establishment model.""" description_translated = TranslatedField() @@ -197,10 +185,10 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer): best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) vintage_year = serializers.ReadOnlyField() - class Meta(EstablishmentListSerializer.Meta): + class Meta(EstablishmentBaseSerializer.Meta): """Meta class.""" - fields = EstablishmentListSerializer.Meta.fields + [ + fields = EstablishmentBaseSerializer.Meta.fields + [ 'description_translated', 'image', 'subtypes', diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index f4558b71..8f5d2a26 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.EstablishmentAllListSerializer + serializer_class = serializers.EstablishmentBaseSerializer def get_queryset(self): """Overridden method 'get_queryset'.""" @@ -70,7 +70,8 @@ class EstablishmentRecentReviewListView(EstablishmentListView): class EstablishmentSimilarListView(EstablishmentListView): """Resource for getting a list of establishments.""" - serializer_class = serializers.EstablishmentListSerializer + + serializer_class = serializers.EstablishmentBaseSerializer pagination_class = EstablishmentPortionPagination def get_queryset(self): @@ -96,6 +97,7 @@ class EstablishmentCommentCreateView(generics.CreateAPIView): class EstablishmentCommentListView(generics.ListAPIView): """View for return list of establishment comments.""" + permission_classes = (permissions.AllowAny,) serializer_class = serializers.EstablishmentCommentCreateSerializer @@ -153,11 +155,13 @@ class EstablishmentFavoritesCreateDestroyView(generics.CreateAPIView, generics.D class EstablishmentNearestRetrieveView(EstablishmentListView, generics.ListAPIView): """Resource for getting list of nearest establishments.""" - serializer_class = serializers.EstablishmentListSerializer + + serializer_class = serializers.EstablishmentBaseSerializer filter_class = filters.EstablishmentFilter def get_queryset(self): """Overridden method 'get_queryset'.""" + # todo: latitude and longitude lat = self.request.query_params.get('lat') lon = self.request.query_params.get('lon') radius = self.request.query_params.get('radius') diff --git a/apps/favorites/views.py b/apps/favorites/views.py index a80960a8..5d99ed4b 100644 --- a/apps/favorites/views.py +++ b/apps/favorites/views.py @@ -1,13 +1,13 @@ """Views for app favorites.""" from rest_framework import generics - from establishment.models import Establishment -from establishment.serializers import EstablishmentListSerializer +from establishment.serializers import EstablishmentBaseSerializer from .models import Favorites class FavoritesBaseView(generics.GenericAPIView): """Base view for Favorites.""" + def get_queryset(self): """Override get_queryset method.""" return Favorites.objects.by_user(self.request.user) @@ -15,7 +15,8 @@ class FavoritesBaseView(generics.GenericAPIView): class FavoritesEstablishmentListView(generics.ListAPIView): """List views for favorites""" - serializer_class = EstablishmentListSerializer + + serializer_class = EstablishmentBaseSerializer def get_queryset(self): """Override get_queryset method""" diff --git a/apps/location/models.py b/apps/location/models.py index 7084385f..1cab1815 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -71,6 +71,7 @@ class City(models.Model): class Address(models.Model): + """Address model.""" city = models.ForeignKey(City, verbose_name=_('city'), on_delete=models.CASCADE) street_name_1 = models.CharField( @@ -98,11 +99,11 @@ class Address(models.Model): @property def latitude(self): - return self.coordinates.y + return self.coordinates.y if self.coordinates else float(0) @property def longitude(self): - return self.coordinates.x + return self.coordinates.x if self.coordinates else float(0) @property def location_field_indexing(self): diff --git a/apps/location/serializers/back.py b/apps/location/serializers/back.py index f3b36e64..f25aacf6 100644 --- a/apps/location/serializers/back.py +++ b/apps/location/serializers/back.py @@ -1,11 +1,8 @@ -from django.contrib.gis.geos import Point -from rest_framework import serializers - from location import models from location.serializers import common -class AddressCreateSerializer(common.AddressSerializer): +class AddressCreateSerializer(common.AddressDetailSerializer): """Address create serializer.""" diff --git a/apps/location/serializers/common.py b/apps/location/serializers/common.py index 37d782de..87d0df4e 100644 --- a/apps/location/serializers/common.py +++ b/apps/location/serializers/common.py @@ -1,5 +1,6 @@ """Location app common serializers.""" from django.contrib.gis.geos import Point +from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from location import models from utils.serializers import TranslatedField @@ -83,55 +84,18 @@ class CitySerializer(serializers.ModelSerializer): ] -class AddressSerializer(serializers.ModelSerializer): - """Address serializer.""" - - city_id = serializers.PrimaryKeyRelatedField( - source='city', - queryset=models.City.objects.all()) - city = CitySerializer(read_only=True) - geo_lon = serializers.FloatField(allow_null=True) - geo_lat = serializers.FloatField(allow_null=True) - - class Meta: - model = models.Address - fields = [ - 'id', - 'city_id', - 'city', - 'street_name_1', - 'street_name_2', - 'number', - 'postal_code', - 'geo_lon', - 'geo_lat', - ] - - def validate(self, attrs): - # if geo_lat and geo_lon was sent - geo_lat = attrs.pop('geo_lat') if 'geo_lat' in attrs else None - geo_lon = attrs.pop('geo_lon') if 'geo_lon' in attrs else None - - if geo_lat and geo_lon: - # Point(longitude, latitude) - attrs['coordinates'] = Point(geo_lat, geo_lon) - return attrs - - def to_representation(self, instance): - """Override to_representation method""" - if instance.coordinates and isinstance(instance.coordinates, Point): - # Point(longitude, latitude) - setattr(instance, 'geo_lat', instance.coordinates.x) - setattr(instance, 'geo_lon', instance.coordinates.y) - else: - setattr(instance, 'geo_lat', float(0)) - setattr(instance, 'geo_lon', float(0)) - return super().to_representation(instance) - - -class AddressSimpleSerializer(serializers.ModelSerializer): +class AddressBaseSerializer(serializers.ModelSerializer): """Serializer for address obj in related objects.""" + latitude = serializers.FloatField(allow_null=True) + longitude = serializers.FloatField(allow_null=True) + + # todo: remove this fields (backward compatibility) + geo_lon = serializers.FloatField(source='longitude', allow_null=True, + read_only=True) + geo_lat = serializers.FloatField(source='latitude', allow_null=True, + read_only=True) + class Meta: """Meta class.""" @@ -142,4 +106,45 @@ class AddressSimpleSerializer(serializers.ModelSerializer): 'street_name_2', 'number', 'postal_code', + 'latitude', + 'longitude', + + # todo: remove this fields (backward compatibility) + 'geo_lon', + 'geo_lat', + ) + + def validate_latitude(self, value): + if -90 <= value <= 90: + return value + raise serializers.ValidationError(_('Invalid value')) + + def validate_longitude(self, value): + if -180 <= value <= 180: + return value + raise serializers.ValidationError(_('Invalid value')) + + def validate(self, attrs): + # validate coordinates + latitude = attrs.pop('latitude', None) + longitude = attrs.pop('longitude', None) + if latitude is not None and longitude is not None: + attrs['coordinates'] = Point(longitude, latitude) + return attrs + + +class AddressDetailSerializer(AddressBaseSerializer): + """Address serializer.""" + + city_id = serializers.PrimaryKeyRelatedField( + source='city', write_only=True, + queryset=models.City.objects.all()) + city = CitySerializer(read_only=True) + + class Meta(AddressBaseSerializer.Meta): + """Meta class.""" + + fields = AddressBaseSerializer.Meta.fields + ( + 'city_id', + 'city', ) diff --git a/apps/location/views/back.py b/apps/location/views/back.py index 69ec28bc..ce6589ed 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -1,6 +1,5 @@ """Location app views.""" from rest_framework import generics -from rest_framework import permissions from location import models, serializers from location.views import common @@ -9,13 +8,13 @@ from location.views import common # Address class AddressListCreateView(common.AddressViewMixin, generics.ListCreateAPIView): """Create view for model Address.""" - serializer_class = serializers.AddressSerializer + serializer_class = serializers.AddressDetailSerializer queryset = models.Address.objects.all() class AddressRUDView(common.AddressViewMixin, generics.RetrieveUpdateDestroyAPIView): """RUD view for model Address.""" - serializer_class = serializers.AddressSerializer + serializer_class = serializers.AddressDetailSerializer queryset = models.Address.objects.all() diff --git a/apps/location/views/common.py b/apps/location/views/common.py index 6bc332ad..792fce91 100644 --- a/apps/location/views/common.py +++ b/apps/location/views/common.py @@ -100,17 +100,17 @@ class CityUpdateView(CityViewMixin, generics.UpdateAPIView): # Address class AddressCreateView(AddressViewMixin, generics.CreateAPIView): """Create view for model Address""" - serializer_class = serializers.AddressSerializer + serializer_class = serializers.AddressDetailSerializer class AddressRetrieveView(AddressViewMixin, generics.RetrieveAPIView): """Retrieve view for model Address""" - serializer_class = serializers.AddressSerializer + serializer_class = serializers.AddressDetailSerializer class AddressListView(AddressViewMixin, generics.ListAPIView): """List view for model Address""" permission_classes = (permissions.AllowAny, ) - serializer_class = serializers.AddressSerializer + serializer_class = serializers.AddressDetailSerializer