From 5793eaad32db7cf90341d2b3d0e349e5a925c949 Mon Sep 17 00:00:00 2001 From: littlewolf Date: Thu, 26 Sep 2019 20:17:50 +0300 Subject: [PATCH 01/10] 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 02/10] 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 03/10] 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 910322273e72b095862e6a8e8ff7ab23c3296084 Mon Sep 17 00:00:00 2001 From: littlewolf Date: Fri, 27 Sep 2019 12:01:11 +0300 Subject: [PATCH 04/10] 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 b55a8ffda6a35886976a971019e4617eca51f04e Mon Sep 17 00:00:00 2001 From: littlewolf Date: Fri, 27 Sep 2019 12:23:12 +0300 Subject: [PATCH 05/10] 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 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 06/10] 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 07/10] 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 08/10] 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 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 09/10] 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 10/10] 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 = [ + ]