From 5793eaad32db7cf90341d2b3d0e349e5a925c949 Mon Sep 17 00:00:00 2001 From: littlewolf Date: Thu, 26 Sep 2019 20:17:50 +0300 Subject: [PATCH 1/7] 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 2/7] 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 3/7] 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 4/7] 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 5/7] 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 a0a6578ae849d5c0b9c96206b9e3ac8d77c0dbdc Mon Sep 17 00:00:00 2001 From: littlewolf Date: Fri, 27 Sep 2019 17:11:42 +0300 Subject: [PATCH 6/7] 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 7/7] 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)\