added filter - establishment_id, refactored guide counters
This commit is contained in:
parent
4604c81ede
commit
61d2f6ec46
56
apps/collection/filters.py
Normal file
56
apps/collection/filters.py
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
"""Collection app filters."""
|
||||||
|
from django_filters import rest_framework as filters
|
||||||
|
from django.core.validators import EMPTY_VALUES
|
||||||
|
|
||||||
|
from collection import models
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionFilterSet(filters.FilterSet):
|
||||||
|
"""Collection filter set."""
|
||||||
|
establishment_id = filters.NumberFilter(
|
||||||
|
field_name='establishments__id',
|
||||||
|
help_text='Establishment id. Allows to filter list of collections by choosen estblishment. '
|
||||||
|
'Use for Establishment detail\'s sheet to content display within '
|
||||||
|
'"Collections & Guides" tab.'
|
||||||
|
)
|
||||||
|
|
||||||
|
# "ordering" instead of "o" is for backward compatibility
|
||||||
|
ordering = filters.OrderingFilter(
|
||||||
|
# tuple-mapping retains order
|
||||||
|
fields=(
|
||||||
|
('rank', 'rank'),
|
||||||
|
('start', 'start'),
|
||||||
|
),
|
||||||
|
help_text='Ordering by fields - rank, start',
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
model = models.Collection
|
||||||
|
fields = (
|
||||||
|
'ordering',
|
||||||
|
'establishment_id',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GuideFilterSet(filters.FilterSet):
|
||||||
|
"""Guide filter set."""
|
||||||
|
establishment_id = filters.NumberFilter(
|
||||||
|
method='by_establishment_id',
|
||||||
|
help_text='Establishment id. Allows to filter list of guides by choosen establishment. '
|
||||||
|
'Use for Establishment detail\'s sheet to content display within '
|
||||||
|
'"Collections & Guides" tab.'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
model = models.Guide
|
||||||
|
fields = (
|
||||||
|
'establishment_id',
|
||||||
|
)
|
||||||
|
|
||||||
|
def by_establishment_id(self, queryset, name, value):
|
||||||
|
"""Filter by establishment id."""
|
||||||
|
if value not in EMPTY_VALUES:
|
||||||
|
return queryset.by_establishment_id(value)
|
||||||
|
return queryset
|
||||||
|
|
@ -171,17 +171,26 @@ class GuideQuerySet(models.QuerySet):
|
||||||
"""Return QuerySet with related."""
|
"""Return QuerySet with related."""
|
||||||
return self.select_related('site', )
|
return self.select_related('site', )
|
||||||
|
|
||||||
|
def with_extended_related(self):
|
||||||
|
"""Return QuerySet with extended related."""
|
||||||
|
return self.with_base_related().prefetch_related('guideelement_set')
|
||||||
|
|
||||||
def by_country_id(self, country_id):
|
def by_country_id(self, country_id):
|
||||||
"""Return QuerySet filtered by country code."""
|
"""Return QuerySet filtered by country code."""
|
||||||
return self.filter(country_json__id__contains=country_id)
|
return self.filter(country_json__id__contains=country_id)
|
||||||
|
|
||||||
def annotate_in_restaurant_section(self):
|
def annotate_in_restaurant_section(self):
|
||||||
"""Annotate flag if GuideElement in RestaurantSectionNode."""
|
"""Annotate flag if GuideElement in RestaurantSectionNode."""
|
||||||
|
restaurant_guides = models.Subquery(
|
||||||
|
self.filter(
|
||||||
|
guideelement__guide_element_type__name='EstablishmentNode',
|
||||||
|
guideelement__parent__guide_element_type__name='RestaurantSectionNode',
|
||||||
|
).values_list('id', flat=True).distinct()
|
||||||
|
)
|
||||||
return self.annotate(
|
return self.annotate(
|
||||||
in_restaurant_section=models.Case(
|
in_restaurant_section=models.Case(
|
||||||
models.When(
|
models.When(
|
||||||
guideelement__guide_element_type__name='EstablishmentNode',
|
id__in=restaurant_guides,
|
||||||
guideelement__parent__guide_element_type__name='RestaurantSectionNode',
|
|
||||||
then=True),
|
then=True),
|
||||||
default=False,
|
default=False,
|
||||||
output_field=models.BooleanField(default=False)
|
output_field=models.BooleanField(default=False)
|
||||||
|
|
@ -190,11 +199,16 @@ class GuideQuerySet(models.QuerySet):
|
||||||
|
|
||||||
def annotate_in_shop_section(self):
|
def annotate_in_shop_section(self):
|
||||||
"""Annotate flag if GuideElement in ShopSectionNode."""
|
"""Annotate flag if GuideElement in ShopSectionNode."""
|
||||||
|
shop_guides = models.Subquery(
|
||||||
|
self.filter(
|
||||||
|
guideelement__guide_element_type__name='EstablishmentNode',
|
||||||
|
guideelement__parent__guide_element_type__name='ShopSectionNode',
|
||||||
|
).values_list('guideelement__id', flat=True).distinct()
|
||||||
|
)
|
||||||
return self.annotate(
|
return self.annotate(
|
||||||
in_shop_section=models.Case(
|
in_shop_section=models.Case(
|
||||||
models.When(
|
models.When(
|
||||||
guideelement__guide_element_type__name='EstablishmentNode',
|
id__in=shop_guides,
|
||||||
guideelement__parent__guide_element_type__name='ShopSectionNode',
|
|
||||||
then=True),
|
then=True),
|
||||||
default=False,
|
default=False,
|
||||||
output_field=models.BooleanField(default=False)
|
output_field=models.BooleanField(default=False)
|
||||||
|
|
@ -205,37 +219,60 @@ class GuideQuerySet(models.QuerySet):
|
||||||
"""Return QuerySet with annotated field - restaurant_counter."""
|
"""Return QuerySet with annotated field - restaurant_counter."""
|
||||||
return self.annotate_in_restaurant_section().annotate(
|
return self.annotate_in_restaurant_section().annotate(
|
||||||
restaurant_counter=models.Count(
|
restaurant_counter=models.Count(
|
||||||
'guideelement',
|
'guideelement__establishment',
|
||||||
filter=models.Q(in_restaurant_section=True) &
|
filter=models.Q(in_restaurant_section=True) &
|
||||||
models.Q(guideelement__parent_id__isnull=False),
|
models.Q(guideelement__parent_id__isnull=True),
|
||||||
distinct=True))
|
distinct=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def annotate_shop_counter(self):
|
def annotate_shop_counter(self):
|
||||||
"""Return QuerySet with annotated field - shop_counter."""
|
"""Return QuerySet with annotated field - shop_counter."""
|
||||||
return self.annotate_in_shop_section().annotate(
|
return self.annotate_in_shop_section().annotate(
|
||||||
shop_counter=models.Count(
|
shop_counter=models.Count(
|
||||||
'guideelement',
|
'guideelement__establishment',
|
||||||
filter=models.Q(in_shop_section=True) &
|
filter=models.Q(in_shop_section=True) &
|
||||||
models.Q(guideelement__parent_id__isnull=False),
|
models.Q(guideelement__parent_id__isnull=True),
|
||||||
distinct=True))
|
distinct=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def annotate_wine_counter(self):
|
def annotate_wine_counter(self):
|
||||||
"""Return QuerySet with annotated field - shop_counter."""
|
"""Return QuerySet with annotated field - shop_counter."""
|
||||||
return self.annotate_in_restaurant_section().annotate(
|
return self.annotate_in_restaurant_section().annotate(
|
||||||
wine_counter=models.Count(
|
wine_counter=models.Count(
|
||||||
'guideelement',
|
'guideelement__product',
|
||||||
filter=models.Q(guideelement__guide_element_type__name='WineNode') &
|
filter=models.Q(guideelement__guide_element_type__name='WineNode') &
|
||||||
models.Q(guideelement__parent_id__isnull=False),
|
models.Q(guideelement__parent_id__isnull=False),
|
||||||
distinct=True))
|
distinct=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def annotate_present_objects_counter(self):
|
def annotate_present_objects_counter(self):
|
||||||
"""Return QuerySet with annotated field - present_objects_counter."""
|
"""Return QuerySet with annotated field - present_objects_counter."""
|
||||||
return self.annotate_in_restaurant_section().annotate(
|
return (
|
||||||
present_objects_counter=models.Count(
|
self.annotate_restaurant_counter()
|
||||||
'guideelement',
|
.annotate_shop_counter()
|
||||||
filter=models.Q(guideelement__guide_element_type__name__in=['EstablishmentNode', 'WineNode']) &
|
.annotate_wine_counter()
|
||||||
models.Q(guideelement__parent_id__isnull=False),
|
.annotate(
|
||||||
distinct=True))
|
present_objects_counter=(
|
||||||
|
models.F('restaurant_counter') +
|
||||||
|
models.F('shop_counter') +
|
||||||
|
models.F('wine_counter')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def annotate_counters(self):
|
||||||
|
return (
|
||||||
|
self.annotate_restaurant_counter()
|
||||||
|
.annotate_shop_counter()
|
||||||
|
.annotate_wine_counter()
|
||||||
|
.annotate_present_objects_counter()
|
||||||
|
)
|
||||||
|
|
||||||
|
def by_establishment_id(self, establishment_id: int):
|
||||||
|
return self.filter(guideelement__establishment=establishment_id).distinct()
|
||||||
|
|
||||||
|
|
||||||
class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
|
class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ class GuideBaseSerializer(serializers.ModelSerializer):
|
||||||
restaurant_counter = serializers.IntegerField(read_only=True)
|
restaurant_counter = serializers.IntegerField(read_only=True)
|
||||||
shop_counter = serializers.IntegerField(read_only=True)
|
shop_counter = serializers.IntegerField(read_only=True)
|
||||||
wine_counter = serializers.IntegerField(read_only=True)
|
wine_counter = serializers.IntegerField(read_only=True)
|
||||||
|
present_objects_counter = serializers.IntegerField(read_only=True)
|
||||||
count_objects_during_init = serializers.IntegerField(read_only=True,
|
count_objects_during_init = serializers.IntegerField(read_only=True,
|
||||||
source='count_related_objects')
|
source='count_related_objects')
|
||||||
|
|
||||||
|
|
@ -131,6 +132,7 @@ class GuideBaseSerializer(serializers.ModelSerializer):
|
||||||
'restaurant_counter',
|
'restaurant_counter',
|
||||||
'shop_counter',
|
'shop_counter',
|
||||||
'wine_counter',
|
'wine_counter',
|
||||||
|
'present_objects_counter',
|
||||||
'count_objects_during_init',
|
'count_objects_during_init',
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework import mixins, permissions, viewsets
|
from rest_framework import mixins, permissions, viewsets
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.filters import OrderingFilter
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from collection import models, serializers
|
from collection import models, serializers, filters
|
||||||
from collection import tasks
|
from collection import tasks
|
||||||
from utils.views import BindObjectMixin
|
from utils.views import BindObjectMixin
|
||||||
|
|
||||||
|
|
@ -34,12 +32,7 @@ class GuideBaseView(generics.GenericAPIView):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Overridden get_queryset method."""
|
"""Overridden get_queryset method."""
|
||||||
return models.Guide.objects.with_base_related() \
|
return models.Guide.objects.with_extended_related().annotate_counters()
|
||||||
.annotate_restaurant_counter() \
|
|
||||||
.annotate_shop_counter() \
|
|
||||||
.annotate_wine_counter() \
|
|
||||||
.annotate_present_objects_counter() \
|
|
||||||
.distinct()
|
|
||||||
|
|
||||||
|
|
||||||
class GuideFilterBaseView(generics.GenericAPIView):
|
class GuideFilterBaseView(generics.GenericAPIView):
|
||||||
|
|
@ -72,17 +65,14 @@ class CollectionBackOfficeViewSet(mixins.CreateModelMixin,
|
||||||
mixins.RetrieveModelMixin,
|
mixins.RetrieveModelMixin,
|
||||||
BindObjectMixin,
|
BindObjectMixin,
|
||||||
CollectionViewSet):
|
CollectionViewSet):
|
||||||
"""ViewSet for Collection model for BackOffice users."""
|
"""ViewSet for Collections list for BackOffice users and Collection create."""
|
||||||
|
|
||||||
permission_classes = (permissions.IsAuthenticated,)
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
queryset = models.Collection.objects.with_base_related()
|
queryset = models.Collection.objects.with_base_related().order_by('-start')
|
||||||
filter_backends = [DjangoFilterBackend, OrderingFilter]
|
filter_class = filters.CollectionFilterSet
|
||||||
serializer_class = serializers.CollectionBackOfficeSerializer
|
serializer_class = serializers.CollectionBackOfficeSerializer
|
||||||
bind_object_serializer_class = serializers.CollectionBindObjectSerializer
|
bind_object_serializer_class = serializers.CollectionBindObjectSerializer
|
||||||
|
|
||||||
ordering_fields = ('rank', 'start')
|
|
||||||
ordering = ('-start', )
|
|
||||||
|
|
||||||
def perform_binding(self, serializer):
|
def perform_binding(self, serializer):
|
||||||
data = serializer.validated_data
|
data = serializer.validated_data
|
||||||
collection = data.pop('collection')
|
collection = data.pop('collection')
|
||||||
|
|
@ -106,7 +96,8 @@ class CollectionBackOfficeViewSet(mixins.CreateModelMixin,
|
||||||
|
|
||||||
class GuideListCreateView(GuideBaseView,
|
class GuideListCreateView(GuideBaseView,
|
||||||
generics.ListCreateAPIView):
|
generics.ListCreateAPIView):
|
||||||
"""View for Guide model for BackOffice users."""
|
"""View for Guides list for BackOffice users and Guide create."""
|
||||||
|
filter_class = filters.GuideFilterSet
|
||||||
|
|
||||||
|
|
||||||
class GuideFilterCreateView(GuideFilterBaseView,
|
class GuideFilterCreateView(GuideFilterBaseView,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
"""Establishment app filters."""
|
"""Establishment app filters."""
|
||||||
from django.core.validators import EMPTY_VALUES
|
from django.core.validators import EMPTY_VALUES
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django_filters import rest_framework as filters, Filter
|
from django_filters import rest_framework as filters
|
||||||
from django_filters.fields import Lookup
|
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
from establishment import models
|
from establishment import models
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ urlpatterns = [
|
||||||
name='create-comment'),
|
name='create-comment'),
|
||||||
path('slug/<slug:slug>/comments/<int:comment_id>/', views.EstablishmentCommentRUDView.as_view(),
|
path('slug/<slug:slug>/comments/<int:comment_id>/', views.EstablishmentCommentRUDView.as_view(),
|
||||||
name='rud-comment'),
|
name='rud-comment'),
|
||||||
path('slug/<slug:slug>/favorites/', views.EstablishmentFavoritesCreateDestroyView.as_view(),
|
path('slug/<slug:slug>/collections/', views.EstablishmentFavoritesCreateDestroyView.as_view(),
|
||||||
name='create-destroy-favorites'),
|
name='create-destroy-favorites'),
|
||||||
|
|
||||||
# similar establishments by type/subtype
|
# similar establishments by type/subtype
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user