gm-259, gm-260, gm-261

This commit is contained in:
Anatoly 2019-10-29 17:16:39 +03:00
parent 13eaa94261
commit 03c75efece
15 changed files with 160 additions and 62 deletions

View File

@ -241,6 +241,20 @@ class User(AbstractUser):
template_name=settings.CHANGE_EMAIL_TEMPLATE, template_name=settings.CHANGE_EMAIL_TEMPLATE,
context=context) context=context)
@property
def favorite_establishment_ids(self):
"""Return establishment IDs that in favorites for current user."""
return self.favorites.by_content_type(app_label='establishment',
model='establishment')\
.values_list('object_id', flat=True)
@property
def favorite_recipe_ids(self):
"""Return recipe IDs that in favorites for current user."""
return self.favorites.by_content_type(app_label='recipe',
model='recipe')\
.values_list('object_id', flat=True)
class UserRole(ProjectBaseMixin): class UserRole(ProjectBaseMixin):
"""UserRole model.""" """UserRole model."""

View File

@ -248,14 +248,12 @@ class EstablishmentQuerySet(models.QuerySet):
def annotate_in_favorites(self, user): def annotate_in_favorites(self, user):
"""Annotate flag in_favorites""" """Annotate flag in_favorites"""
favorite_establishments = [] favorite_establishment_ids = []
if user.is_authenticated: if user.is_authenticated:
favorite_establishments = user.favorites.by_content_type(app_label='establishment', favorite_establishment_ids = user.favorite_establishment_ids
model='establishment') \
.values_list('object_id', flat=True)
return self.annotate(in_favorites=Case( return self.annotate(in_favorites=Case(
When( When(
id__in=favorite_establishments, id__in=favorite_establishment_ids,
then=True), then=True),
default=False, default=False,
output_field=models.BooleanField(default=False))) output_field=models.BooleanField(default=False)))

View File

@ -5,15 +5,14 @@ from rest_framework import serializers
from comment import models as comment_models from comment import models as comment_models
from comment.serializers import common as comment_serializers from comment.serializers import common as comment_serializers
from establishment import models from establishment import models
from favorites.models import Favorites
from location.serializers import AddressBaseSerializer from location.serializers import AddressBaseSerializer
from main.serializers import AwardSerializer, CurrencySerializer from main.serializers import AwardSerializer, CurrencySerializer
from review import models as review_models from review import models as review_models
from tag.serializers import TagBaseSerializer from tag.serializers import TagBaseSerializer
from timetable.serialziers import ScheduleRUDSerializer from timetable.serialziers import ScheduleRUDSerializer
from utils import exceptions as utils_exceptions from utils import exceptions as utils_exceptions
from utils.serializers import ProjectModelSerializer from utils.serializers import (ProjectModelSerializer, TranslatedField,
from utils.serializers import TranslatedField FavoritesCreateSerializer)
class ContactPhonesSerializer(serializers.ModelSerializer): class ContactPhonesSerializer(serializers.ModelSerializer):
@ -281,26 +280,13 @@ class EstablishmentCommentRUDSerializer(comment_serializers.CommentSerializer):
] ]
class EstablishmentFavoritesCreateSerializer(serializers.ModelSerializer): class EstablishmentFavoritesCreateSerializer(FavoritesCreateSerializer):
"""Create comment serializer""" """Serializer to favorite object w/ model Establishment."""
class Meta:
"""Serializer for model Comment"""
model = Favorites
fields = [
'id',
'created',
]
def get_user(self):
"""Get user from request"""
return self.context.get('request').user
def validate(self, attrs): def validate(self, attrs):
"""Override validate method""" """Overridden validate method"""
# Check establishment object # Check establishment object
establishment_slug = self.context.get('request').parser_context.get('kwargs').get('slug') establishment_qs = models.Establishment.objects.filter(slug=self.slug)
establishment_qs = models.Establishment.objects.filter(slug=establishment_slug)
# Check establishment obj by slug from lookup_kwarg # Check establishment obj by slug from lookup_kwarg
if not establishment_qs.exists(): if not establishment_qs.exists():
@ -309,18 +295,16 @@ class EstablishmentFavoritesCreateSerializer(serializers.ModelSerializer):
establishment = establishment_qs.first() establishment = establishment_qs.first()
# Check existence in favorites # Check existence in favorites
if self.get_user().favorites.by_content_type(app_label='establishment', if establishment.favorites.filter(user=self.user).exists():
model='establishment')\
.by_object_id(object_id=establishment.id).exists():
raise utils_exceptions.FavoritesError() raise utils_exceptions.FavoritesError()
attrs['establishment'] = establishment attrs['establishment'] = establishment
return attrs return attrs
def create(self, validated_data, *args, **kwargs): def create(self, validated_data, *args, **kwargs):
"""Override create method""" """Overridden create method"""
validated_data.update({ validated_data.update({
'user': self.get_user(), 'user': self.user,
'content_object': validated_data.pop('establishment') 'content_object': validated_data.pop('establishment')
}) })
return super().create(validated_data) return super().create(validated_data)

View File

@ -9,7 +9,6 @@ urlpatterns = [
path('', views.EstablishmentListView.as_view(), name='list'), path('', views.EstablishmentListView.as_view(), name='list'),
path('recent-reviews/', views.EstablishmentRecentReviewListView.as_view(), path('recent-reviews/', views.EstablishmentRecentReviewListView.as_view(),
name='recent-reviews'), name='recent-reviews'),
# path('wineries/', views.WineriesListView.as_view(), name='wineries-list'),
path('slug/<slug:slug>/', views.EstablishmentRetrieveView.as_view(), name='detail'), path('slug/<slug:slug>/', views.EstablishmentRetrieveView.as_view(), name='detail'),
path('slug/<slug:slug>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'), path('slug/<slug:slug>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'),
path('slug/<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'), path('slug/<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'),
@ -18,5 +17,5 @@ urlpatterns = [
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>/favorites/', views.EstablishmentFavoritesCreateDestroyView.as_view(),
name='add-to-favorites') name='create-destroy-favorites')
] ]

View File

@ -138,15 +138,12 @@ class EstablishmentFavoritesCreateDestroyView(generics.CreateAPIView, generics.D
""" """
Returns the object the view is displaying. Returns the object the view is displaying.
""" """
establishment_obj = get_object_or_404(models.Establishment, establishment = get_object_or_404(models.Establishment,
slug=self.kwargs['slug']) slug=self.kwargs['slug'])
obj = get_object_or_404( favorites = get_object_or_404(establishment.favorites.filter(user=self.request.user))
self.request.user.favorites.by_content_type(app_label='establishment',
model='establishment')
.by_object_id(object_id=establishment_obj.pk))
# May raise a permission denied # May raise a permission denied
self.check_object_permissions(self.request, obj) self.check_object_permissions(self.request, favorites)
return obj return favorites
class EstablishmentNearestRetrieveView(EstablishmentListView, generics.ListAPIView): class EstablishmentNearestRetrieveView(EstablishmentListView, generics.ListAPIView):
@ -170,14 +167,3 @@ class EstablishmentNearestRetrieveView(EstablishmentListView, generics.ListAPIVi
return qs.by_distance_from_point(**{k: v for k, v in filter_kwargs.items() return qs.by_distance_from_point(**{k: v for k, v in filter_kwargs.items()
if v is not None}) if v is not None})
return qs return qs
# Wineries
# todo: find out about difference between subtypes data
# class WineriesListView(EstablishmentListView):
# """Return list establishments with type Wineries"""
#
# def get_queryset(self):
# """Overridden get_queryset method."""
# qs = super(WineriesListView, self).get_queryset()
# return qs.with_type_related().wineries()

View File

@ -8,4 +8,6 @@ app_name = 'favorites'
urlpatterns = [ urlpatterns = [
path('establishments/', views.FavoritesEstablishmentListView.as_view(), path('establishments/', views.FavoritesEstablishmentListView.as_view(),
name='establishment-list'), name='establishment-list'),
path('products/', views.FavoritesProductListView.as_view(),
name='product-list'),
] ]

View File

@ -3,6 +3,9 @@ from rest_framework import generics
from establishment.models import Establishment from establishment.models import Establishment
from establishment.filters import EstablishmentFilter from establishment.filters import EstablishmentFilter
from establishment.serializers import EstablishmentBaseSerializer from establishment.serializers import EstablishmentBaseSerializer
from product.models import Product
from product.serializers import ProductBaseSerializer
from product.filters import ProductFilterSet
from .models import Favorites from .models import Favorites
@ -15,7 +18,7 @@ class FavoritesBaseView(generics.GenericAPIView):
class FavoritesEstablishmentListView(generics.ListAPIView): class FavoritesEstablishmentListView(generics.ListAPIView):
"""List views for favorites""" """List views for establishments in favorites."""
serializer_class = EstablishmentBaseSerializer serializer_class = EstablishmentBaseSerializer
filter_class = EstablishmentFilter filter_class = EstablishmentFilter
@ -24,3 +27,15 @@ class FavoritesEstablishmentListView(generics.ListAPIView):
"""Override get_queryset method""" """Override get_queryset method"""
return Establishment.objects.filter(favorites__user=self.request.user)\ return Establishment.objects.filter(favorites__user=self.request.user)\
.order_by('-favorites') .order_by('-favorites')
class FavoritesProductListView(generics.ListAPIView):
"""List views for products in favorites."""
serializer_class = ProductBaseSerializer
filter_class = ProductFilterSet
def get_queryset(self):
"""Override get_queryset method"""
return Product.objects.filter(favorites__user=self.request.user)\
.order_by('-favorites')

View File

@ -5,7 +5,7 @@ from django_filters import rest_framework as filters
from product import models from product import models
class ProductListFilterSet(filters.FilterSet): class ProductFilterSet(filters.FilterSet):
"""Product filter set.""" """Product filter set."""
establishment_id = filters.NumberFilter() establishment_id = filters.NumberFilter()

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-10-29 14:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='product',
name='slug',
field=models.SlugField(max_length=255, null=True, unique=True, verbose_name='Establishment slug'),
),
]

View File

@ -1,5 +1,6 @@
"""Product app models.""" """Product app models."""
from django.db import models from django.db import models
from django.contrib.contenttypes import fields as generic
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields import JSONField
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -141,6 +142,9 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
wine_appellation = models.ForeignKey('location.WineAppellation', on_delete=models.PROTECT, wine_appellation = models.ForeignKey('location.WineAppellation', on_delete=models.PROTECT,
blank=True, null=True, blank=True, null=True,
verbose_name=_('wine appellation')) verbose_name=_('wine appellation'))
slug = models.SlugField(unique=True, max_length=255, null=True,
verbose_name=_('Establishment slug'))
favorites = generic.GenericRelation(to='favorites.Favorites')
objects = ProductManager.from_queryset(ProductQuerySet)() objects = ProductManager.from_queryset(ProductQuerySet)()

View File

@ -1,9 +1,11 @@
"""Product app serializers.""" """Product app serializers."""
from rest_framework import serializers from rest_framework import serializers
from utils.serializers import TranslatedField from utils.serializers import TranslatedField, FavoritesCreateSerializer
from product.models import Product, ProductSubType, ProductType from product.models import Product, ProductSubType, ProductType
from utils import exceptions as utils_exceptions
from django.utils.translation import gettext_lazy as _
from location.serializers import (WineRegionBaseSerializer, WineAppellationBaseSerializer, from location.serializers import (WineRegionBaseSerializer, WineAppellationBaseSerializer,
CountrySimpleSerializer) CountrySimpleSerializer)
class ProductSubTypeBaseSerializer(serializers.ModelSerializer): class ProductSubTypeBaseSerializer(serializers.ModelSerializer):
@ -50,6 +52,7 @@ class ProductBaseSerializer(serializers.ModelSerializer):
model = Product model = Product
fields = [ fields = [
'id', 'id',
'slug',
'name_translated', 'name_translated',
'category_display', 'category_display',
'description_translated', 'description_translated',
@ -61,3 +64,33 @@ class ProductBaseSerializer(serializers.ModelSerializer):
'wine_appellation', 'wine_appellation',
'available_countries', 'available_countries',
] ]
class ProductFavoritesCreateSerializer(FavoritesCreateSerializer):
"""Serializer to create favorite object w/ model Product."""
def validate(self, attrs):
"""Overridden validate method"""
# Check establishment object
product_qs = Product.objects.filter(slug=self.slug)
# Check establishment obj by slug from lookup_kwarg
if not product_qs.exists():
raise serializers.ValidationError({'detail': _('Object not found.')})
else:
product = product_qs.first()
# Check existence in favorites
if product.favorites.filter(user=self.user).exists():
raise utils_exceptions.FavoritesError()
attrs['product'] = product
return attrs
def create(self, validated_data, *args, **kwargs):
"""Overridden create method"""
validated_data.update({
'user': self.user,
'content_object': validated_data.pop('product')
})
return super().create(validated_data)

View File

@ -6,5 +6,7 @@ from product import views
app_name = 'product' app_name = 'product'
urlpatterns = [ urlpatterns = [
path('', views.ProductListView.as_view(), name='list') path('', views.ProductListView.as_view(), name='list'),
path('slug/<slug:slug>/favorites/', views.CreateFavoriteProductView.as_view(),
name='create-destroy-favorites')
] ]

View File

@ -1,5 +1,6 @@
"""Product app views.""" """Product app views."""
from rest_framework import generics, permissions from rest_framework import generics, permissions
from django.shortcuts import get_object_or_404
from product.models import Product from product.models import Product
from product import serializers from product import serializers
from product import filters from product import filters
@ -17,4 +18,22 @@ class ProductListView(ProductBaseView, generics.ListAPIView):
"""List view for model Product.""" """List view for model Product."""
permission_classes = (permissions.AllowAny, ) permission_classes = (permissions.AllowAny, )
serializer_class = serializers.ProductBaseSerializer serializer_class = serializers.ProductBaseSerializer
filter_class = filters.ProductListFilterSet filter_class = filters.ProductFilterSet
class CreateFavoriteProductView(generics.CreateAPIView,
generics.DestroyAPIView):
"""View for create/destroy product in favorites."""
serializer_class = serializers.ProductFavoritesCreateSerializer
lookup_field = 'slug'
def get_object(self):
"""
Returns the object the view is displaying.
"""
product = get_object_or_404(Product, slug=self.kwargs['slug'])
favorites = get_object_or_404(product.favorites.filter(user=self.request.user))
# May raise a permission denied
self.check_object_permissions(self.request, favorites)
return favorites

View File

@ -15,14 +15,12 @@ class RecipeQuerySet(models.QuerySet):
def annotate_in_favorites(self, user): def annotate_in_favorites(self, user):
"""Annotate flag in_favorites""" """Annotate flag in_favorites"""
favorite_establishments = [] favorite_recipe_ids = []
if user.is_authenticated: if user.is_authenticated:
favorite_establishments = user.favorites.by_content_type(app_label='recipe', favorite_recipe_ids = user.favorite_recipe_ids
model='recipe') \
.values_list('object_id', flat=True)
return self.annotate(in_favorites=models.Case( return self.annotate(in_favorites=models.Case(
models.When( models.When(
id__in=favorite_establishments, id__in=favorite_recipe_ids,
then=True), then=True),
default=False, default=False,
output_field=models.BooleanField(default=False))) output_field=models.BooleanField(default=False)))

View File

@ -4,6 +4,7 @@ from django.core import exceptions
from rest_framework import serializers from rest_framework import serializers
from utils import models from utils import models
from translation.models import Language from translation.models import Language
from favorites.models import Favorites
class EmptySerializer(serializers.Serializer): class EmptySerializer(serializers.Serializer):
@ -72,3 +73,28 @@ class ProjectModelSerializer(serializers.ModelSerializer):
"""Overrided ModelSerializer.""" """Overrided ModelSerializer."""
serializers.ModelSerializer.serializer_field_mapping[models.TJSONField] = TJSONField serializers.ModelSerializer.serializer_field_mapping[models.TJSONField] = TJSONField
class FavoritesCreateSerializer(serializers.ModelSerializer):
"""Serializer to favorite object."""
class Meta:
"""Serializer for model Comment."""
model = Favorites
fields = [
'id',
'created',
]
@property
def request(self):
return self.context.get('request')
@property
def user(self):
"""Get user from request"""
return self.request.user
@property
def slug(self):
return self.request.parser_context.get('kwargs').get('slug')