From e067e8e793eda39c5910411d76c73d7cea50bf4b Mon Sep 17 00:00:00 2001 From: dormantman Date: Tue, 28 Jan 2020 18:29:02 +0300 Subject: [PATCH 1/5] Added menu upload files --- apps/establishment/models.py | 59 ++++--------- apps/establishment/serializers/back.py | 94 ++++++++++---------- apps/establishment/serializers/common.py | 56 ++++++++++-- apps/establishment/urls/back.py | 6 +- apps/establishment/views/back.py | 105 ++++++++++++----------- apps/utils/methods.py | 11 ++- apps/utils/models.py | 33 ++++++- 7 files changed, 210 insertions(+), 154 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index b1adf435..83a7c5e8 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -30,10 +30,13 @@ from main.models import Award, Currency from review.models import Review from tag.models import Tag from utils.methods import transform_into_readable_str -from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, - TranslatedFieldsMixin, BaseAttributes, GalleryMixin, - IntermediateGalleryModelMixin, HasTagsMixin, - FavoritesMixin, TypeDefaultImageMixin) +from utils.models import ( + ProjectBaseMixin, TJSONField, URLImageMixin, + TranslatedFieldsMixin, BaseAttributes, GalleryMixin, + IntermediateGalleryModelMixin, HasTagsMixin, + FavoritesMixin, TypeDefaultImageMixin, FileMixin, + ImageMixin, +) # todo: establishment type&subtypes check @@ -1272,6 +1275,7 @@ class Menu(GalleryMixin, TranslatedFieldsMixin, BaseAttributes): STR_FIELD_NAME = 'category' + name = models.CharField(_('name'), max_length=255, default='') category = TJSONField( blank=True, null=True, default=None, verbose_name=_('category'), help_text='{"en-GB":"some text"}') @@ -1282,9 +1286,14 @@ class Menu(GalleryMixin, TranslatedFieldsMixin, BaseAttributes): schedule = models.ManyToManyField( to='timetable.Timetable', blank=True, - verbose_name=_('Establishment schedule'), - related_name='menus', + verbose_name=_('Menu schedule'), ) + uploads = models.ManyToManyField( + to='MenuFiles', + blank=True, + verbose_name=_('Menu files'), + ) + old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) objects = MenuQuerySet.as_manager() @@ -1295,43 +1304,11 @@ class Menu(GalleryMixin, TranslatedFieldsMixin, BaseAttributes): ordering = ('-created',) -class MenuGallery(IntermediateGalleryModelMixin): - menu = models.ForeignKey( - Menu, - null=True, - related_name='menu_gallery', - on_delete=models.CASCADE, - verbose_name=_('menu'), - ) - image = models.ForeignKey( - 'gallery.Image', - null=True, - related_name='menu_gallery', - on_delete=models.CASCADE, - verbose_name=_('image'), - ) - - class Meta: - """Meta class.""" - verbose_name = _('menu gallery') - verbose_name_plural = _('menu galleries') - unique_together = (('menu', 'is_main'), ('menu', 'image')) - - -class MenuUploads(BaseAttributes): +class MenuFiles(FileMixin, BaseAttributes): """Menu files""" - menu = models.ForeignKey( - Menu, - verbose_name=_('menu'), - on_delete=models.CASCADE, - related_name='menu_uploads', - ) - title = models.CharField(_('title'), max_length=255, default='') - file = models.FileField( - _('File'), - validators=[FileExtensionValidator(allowed_extensions=('doc', 'docx', 'pdf')), ], - ) + name = models.CharField(_('name'), max_length=255, default='') + type = models.CharField(_('type'), max_length=65, default='') class Meta: verbose_name = _('menu upload') diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 8d02f252..e3adb490 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -639,50 +639,50 @@ class MenuDishesRUDSerializers(ProjectModelSerializer): ] -class MenuGallerySerializer(serializers.ModelSerializer): - """Serializer class for model MenuGallery.""" - - class Meta: - """Meta class""" - - model = models.MenuGallery - fields = [ - 'id', - 'is_main', - ] - - @property - def request_kwargs(self): - """Get url kwargs from request.""" - return self.context.get('request').parser_context.get('kwargs') - - def create(self, validated_data): - menu_pk = self.request_kwargs.get('pk') - image_id = self.request_kwargs.get('image_id') - qs = models.MenuGallery.objects.filter(image_id=image_id, menu_id=menu_pk) - instance = qs.first() - if instance: - qs.update(**validated_data) - return instance - return super().create(validated_data) - - def validate(self, attrs): - """Override validate method.""" - menu_pk = self.request_kwargs.get('pk') - image_id = self.request_kwargs.get('image_id') - - menu_qs = models.Menu.objects.filter(pk=menu_pk) - image_qs = Image.objects.filter(id=image_id) - - if not menu_qs.exists(): - raise serializers.ValidationError({'detail': _('Menu not found')}) - if not image_qs.exists(): - raise serializers.ValidationError({'detail': _('Image not found')}) - - menu = menu_qs.first() - image = image_qs.first() - - attrs['menu'] = menu - attrs['image'] = image - - return attrs +# class MenuGallerySerializer(serializers.ModelSerializer): +# """Serializer class for model MenuGallery.""" +# +# class Meta: +# """Meta class""" +# +# model = models.MenuGallery +# fields = [ +# 'id', +# 'is_main', +# ] +# +# @property +# def request_kwargs(self): +# """Get url kwargs from request.""" +# return self.context.get('request').parser_context.get('kwargs') +# +# def create(self, validated_data): +# menu_pk = self.request_kwargs.get('pk') +# image_id = self.request_kwargs.get('image_id') +# qs = models.MenuGallery.objects.filter(image_id=image_id, menu_id=menu_pk) +# instance = qs.first() +# if instance: +# qs.update(**validated_data) +# return instance +# return super().create(validated_data) +# +# def validate(self, attrs): +# """Override validate method.""" +# menu_pk = self.request_kwargs.get('pk') +# image_id = self.request_kwargs.get('image_id') +# +# menu_qs = models.Menu.objects.filter(pk=menu_pk) +# image_qs = Image.objects.filter(id=image_id) +# +# if not menu_qs.exists(): +# raise serializers.ValidationError({'detail': _('Menu not found')}) +# if not image_qs.exists(): +# raise serializers.ValidationError({'detail': _('Image not found')}) +# +# menu = menu_qs.first() +# image = image_qs.first() +# +# attrs['menu'] = menu +# attrs['image'] = image +# +# return attrs diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 5d53eec8..4bb1a8d4 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -9,18 +9,25 @@ from rest_framework import serializers from comment import models as comment_models from comment.serializers import common as comment_serializers from establishment import models -from location.serializers import AddressBaseSerializer, CityBaseSerializer, AddressDetailSerializer, \ - CityShortSerializer -from location.serializers import EstablishmentWineRegionBaseSerializer, \ - EstablishmentWineOriginBaseSerializer +from establishment.models import Establishment +from location.serializers import ( + AddressBaseSerializer, CityBaseSerializer, AddressDetailSerializer, + CityShortSerializer, +) +from location.serializers import ( + EstablishmentWineRegionBaseSerializer, + EstablishmentWineOriginBaseSerializer, +) from main.serializers import AwardSerializer, CurrencySerializer from review.serializers import ReviewShortSerializer from tag.serializers import TagBaseSerializer from timetable.serialziers import ScheduleRUDSerializer from utils import exceptions as utils_exceptions from utils.serializers import ImageBaseSerializer, CarouselCreateSerializer -from utils.serializers import (ProjectModelSerializer, TranslatedField, - FavoritesCreateSerializer) +from utils.serializers import ( + ProjectModelSerializer, TranslatedField, + FavoritesCreateSerializer, +) logger = logging.getLogger(__name__) @@ -71,19 +78,50 @@ class PlateSerializer(ProjectModelSerializer): class MenuSerializers(ProjectModelSerializer): - plates = PlateSerializer(read_only=True, many=True, source='plate_set') + name = serializers.CharField() + establishment_id = serializers.PrimaryKeyRelatedField(queryset=models.Establishment.objects.all()) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + price = serializers.IntegerField(read_only=True, source='establishment.price_level', required=False) + drinks_included = serializers.BooleanField(source='is_drinks_included', required=FavoritesCreateSerializer) + schedules = ScheduleRUDSerializer(many=True, allow_null=True, required=False) + uploads = serializers.FileField(allow_null=True, required=False) category_translated = serializers.CharField(read_only=True) class Meta: model = models.Menu fields = [ 'id', + 'name', + 'establishment', + 'establishment_id', + 'establishment_slug', + 'price', + 'drinks_included', + 'schedules', + 'uploads', 'category', 'category_translated', - 'plates', - 'establishment' ] + def create(self, validated_data): + print(validated_data, '\n\n\n\n\n') + establishment = models.Establishment.objects.get(pk=validated_data.pop('establishment')) + validated_data['establishment'] = establishment['id'] + instance = models.Menu.objects.create(**validated_data) + return instance + +''' +{ + "name": "Menu test", + "establishment_id": 1, + "price": 1, + "drinks_included": true, + "schedules": [], + "uploads": null, + "category": null +} +''' + class MenuRUDSerializers(ProjectModelSerializer): plates = PlateSerializer(read_only=True, many=True, source='plate_set') diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index d5173942..6db9d07a 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -32,9 +32,9 @@ urlpatterns = [ name='establishment-admin-list'), path('menus/dishes/', views.MenuDishesListCreateView.as_view(), name='menu-dishes-list'), path('menus/dishes//', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'), - path('menus/dishes//gallery/', views.MenuGalleryListView.as_view(), name='menu-dishes-gallery-list'), - path('menus/dishes//gallery//', views.MenuGalleryCreateDestroyView.as_view(), - name='menu-dishes-gallery-create-destroy'), + # path('menus/dishes//gallery/', views.MenuGalleryListView.as_view(), name='menu-dishes-gallery-list'), + # path('menus/dishes//gallery//', views.MenuGalleryCreateDestroyView.as_view(), + # name='menu-dishes-gallery-create-destroy'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), path('plates/', views.PlateListCreateView.as_view(), name='plates'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index d745f5e9..b56ebf10 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -125,6 +125,9 @@ class MenuListCreateView(generics.ListCreateAPIView): 'establishment__slug', ) + def get_queryset(self): + return super().get_queryset().prefetch_related('establishment') + class MenuRUDView(generics.RetrieveUpdateDestroyAPIView): """Menu RUD view.""" @@ -464,54 +467,54 @@ class MenuDishesRUDView(generics.RetrieveUpdateDestroyAPIView): permission_classes = [IsWineryReviewer | IsEstablishmentManager] -class MenuGalleryListView(generics.ListAPIView): - """Resource for returning gallery for menu for back-office users.""" - serializer_class = serializers.ImageBaseSerializer - permission_classes = [IsWineryReviewer | IsEstablishmentManager] - queryset = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes() - - def get_object(self): - """Override get_object method.""" - qs = super(MenuGalleryListView, self).get_queryset() - menu = get_object_or_404(qs, pk=self.kwargs.get('pk')) - - # May raise a permission denied - # self.check_object_permissions(self.request, menu) - - return menu - - def get_queryset(self): - """Override get_queryset method.""" - return self.get_object().crop_gallery - - -class MenuGalleryCreateDestroyView(CreateDestroyGalleryViewMixin): - """Resource for a create gallery for menu for back-office users.""" - serializer_class = serializers.MenuGallerySerializer - permission_classes = [IsWineryReviewer | IsEstablishmentManager] - - def get_queryset(self): - """Override get_queryset method.""" - qs = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes() - return qs - - def create(self, request, *args, **kwargs): - _ = super().create(request, *args, **kwargs) - news_qs = self.filter_queryset(self.get_queryset()) - return response.Response( - data=serializers.MenuDishesRUDSerializers(get_object_or_404(news_qs, pk=kwargs.get('pk'))).data - ) - - def get_object(self): - """ - Returns the object the view is displaying. - """ - menu_qs = self.filter_queryset(self.get_queryset()) - - menu = get_object_or_404(menu_qs, pk=self.kwargs.get('pk')) - gallery = get_object_or_404(menu.menu_gallery, image_id=self.kwargs.get('image_id')) - - # May raise a permission denied - self.check_object_permissions(self.request, gallery) - - return gallery +# class MenuGalleryListView(generics.ListAPIView): +# """Resource for returning gallery for menu for back-office users.""" +# serializer_class = serializers.ImageBaseSerializer +# permission_classes = [IsWineryReviewer | IsEstablishmentManager] +# queryset = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes() +# +# def get_object(self): +# """Override get_object method.""" +# qs = super(MenuGalleryListView, self).get_queryset() +# menu = get_object_or_404(qs, pk=self.kwargs.get('pk')) +# +# # May raise a permission denied +# # self.check_object_permissions(self.request, menu) +# +# return menu +# +# def get_queryset(self): +# """Override get_queryset method.""" +# return self.get_object().crop_gallery +# +# +# class MenuGalleryCreateDestroyView(CreateDestroyGalleryViewMixin): +# """Resource for a create gallery for menu for back-office users.""" +# serializer_class = serializers.MenuGallerySerializer +# permission_classes = [IsWineryReviewer | IsEstablishmentManager] +# +# def get_queryset(self): +# """Override get_queryset method.""" +# qs = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes() +# return qs +# +# def create(self, request, *args, **kwargs): +# _ = super().create(request, *args, **kwargs) +# news_qs = self.filter_queryset(self.get_queryset()) +# return response.Response( +# data=serializers.MenuDishesRUDSerializers(get_object_or_404(news_qs, pk=kwargs.get('pk'))).data +# ) +# +# def get_object(self): +# """ +# Returns the object the view is displaying. +# """ +# menu_qs = self.filter_queryset(self.get_queryset()) +# +# menu = get_object_or_404(menu_qs, pk=self.kwargs.get('pk')) +# gallery = get_object_or_404(menu.menu_gallery, image_id=self.kwargs.get('image_id')) +# +# # May raise a permission denied +# self.check_object_permissions(self.request, gallery) +# +# return gallery diff --git a/apps/utils/methods.py b/apps/utils/methods.py index 58cc3f52..839105e2 100644 --- a/apps/utils/methods.py +++ b/apps/utils/methods.py @@ -6,7 +6,7 @@ import string from collections import namedtuple from functools import reduce from io import BytesIO - +import pathlib import requests from django.conf import settings from django.contrib.contenttypes.models import ContentType @@ -69,6 +69,15 @@ def username_random(): ) +def file_path(instance, filename): + """Determine file path method.""" + filename = '%s.%s' % (generate_code(), pathlib.Path(filename).suffix.lstrip('.')) + return 'files/%s/%s/%s' % ( + instance._meta.model_name, + datetime.now().strftime(settings.REST_DATE_FORMAT), + filename) + + def image_path(instance, filename): """Determine avatar path method.""" filename = '%s.jpeg' % generate_code() diff --git a/apps/utils/models.py b/apps/utils/models.py index d58a1bbc..281c57ca 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -8,6 +8,7 @@ from django.contrib.gis.db import models from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields.jsonb import KeyTextTransform +from django.core.validators import FileExtensionValidator from django.utils import timezone from django.utils.html import mark_safe from django.utils.translation import ugettext_lazy as _, get_language @@ -16,7 +17,7 @@ from sorl.thumbnail import get_thumbnail from sorl.thumbnail.fields import ImageField as SORLImageField from configuration.models import TranslationSettings -from utils.methods import image_path, svg_image_path +from utils.methods import image_path, svg_image_path, file_path from utils.validators import svg_image_validator logger = logging.getLogger(__name__) @@ -86,6 +87,7 @@ def translate_field(self, field_name, toggle_field_name=None): return None return value return None + return translate @@ -159,6 +161,33 @@ class BaseAttributes(ProjectBaseMixin): abstract = True +class FileMixin(models.Model): + """File model.""" + + file = models.FileField(upload_to=file_path, + blank=True, null=True, default=None, + verbose_name=_('File'), + validators=[FileExtensionValidator( + allowed_extensions=('jpg', 'jpeg', 'png', 'doc', 'docx', 'pdf') + )]) + + class Meta: + """Meta class.""" + + abstract = True + + def get_file_url(self): + """Get file url.""" + return self.file.url if self.file else None + + def get_full_file_url(self, request): + """Get full file url""" + if self.file and exists(self.file.path): + return request.build_absolute_uri(self.file.url) + else: + return None + + class ImageMixin(models.Model): """Avatar model.""" @@ -230,7 +259,7 @@ class SORLImageMixin(models.Model): else: return None - def get_cropped_image(self, geometry: str, quality: int, cropbox:str) -> dict: + def get_cropped_image(self, geometry: str, quality: int, cropbox: str) -> dict: cropped_image = get_thumbnail(self.image, geometry_string=geometry, # crop=crop, From bcc5f2da51529b84a6a359108481f31857cae7f7 Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 29 Jan 2020 15:17:15 +0300 Subject: [PATCH 2/5] Added menus method --- .../migrations/0084_auto_20200129_1113.py | 69 ++++++++++++++++ .../migrations/0085_menu_price.py | 18 +++++ apps/establishment/models.py | 7 +- apps/establishment/serializers/common.py | 80 +++++++++++-------- apps/establishment/urls/back.py | 2 + apps/establishment/views/back.py | 17 +++- 6 files changed, 155 insertions(+), 38 deletions(-) create mode 100644 apps/establishment/migrations/0084_auto_20200129_1113.py create mode 100644 apps/establishment/migrations/0085_menu_price.py diff --git a/apps/establishment/migrations/0084_auto_20200129_1113.py b/apps/establishment/migrations/0084_auto_20200129_1113.py new file mode 100644 index 00000000..c041c7bd --- /dev/null +++ b/apps/establishment/migrations/0084_auto_20200129_1113.py @@ -0,0 +1,69 @@ +# Generated by Django 2.2.7 on 2020-01-29 11:13 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import utils.methods + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('establishment', '0083_establishment_instagram'), + ] + + operations = [ + migrations.CreateModel( + name='MenuFiles', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('file', models.FileField(blank=True, default=None, null=True, upload_to=utils.methods.file_path, validators=[django.core.validators.FileExtensionValidator(allowed_extensions=('jpg', 'jpeg', 'png', 'doc', 'docx', 'pdf'))], verbose_name='File')), + ('name', models.CharField(default='', max_length=255, verbose_name='name')), + ('type', models.CharField(default='', max_length=65, verbose_name='type')), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menufiles_records_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')), + ('modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menufiles_records_modified', to=settings.AUTH_USER_MODEL, verbose_name='modified by')), + ], + options={ + 'verbose_name': 'menu upload', + 'verbose_name_plural': 'menu uploads', + }, + ), + migrations.RemoveField( + model_name='menuuploads', + name='created_by', + ), + migrations.RemoveField( + model_name='menuuploads', + name='menu', + ), + migrations.RemoveField( + model_name='menuuploads', + name='modified_by', + ), + migrations.AddField( + model_name='menu', + name='name', + field=models.CharField(default='', max_length=255, verbose_name='name'), + ), + migrations.AlterField( + model_name='menu', + name='schedule', + field=models.ManyToManyField(blank=True, to='timetable.Timetable', verbose_name='Menu schedule'), + ), + migrations.DeleteModel( + name='MenuGallery', + ), + migrations.DeleteModel( + name='MenuUploads', + ), + migrations.AddField( + model_name='menu', + name='uploads', + field=models.ManyToManyField(blank=True, to='establishment.MenuFiles', verbose_name='Menu files'), + ), + ] diff --git a/apps/establishment/migrations/0085_menu_price.py b/apps/establishment/migrations/0085_menu_price.py new file mode 100644 index 00000000..23afcba4 --- /dev/null +++ b/apps/establishment/migrations/0085_menu_price.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2020-01-29 11:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0084_auto_20200129_1113'), + ] + + operations = [ + migrations.AddField( + model_name='menu', + name='price', + field=models.IntegerField(blank=True, default=None, null=True, verbose_name='price'), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 83a7c5e8..9f5fa7fd 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -1283,6 +1283,7 @@ class Menu(GalleryMixin, TranslatedFieldsMixin, BaseAttributes): 'establishment.Establishment', verbose_name=_('establishment'), on_delete=models.CASCADE) is_drinks_included = models.BooleanField(_('is drinks included'), default=False) + price = models.IntegerField(blank=True, null=True, default=None, verbose_name=_('price')) schedule = models.ManyToManyField( to='timetable.Timetable', blank=True, @@ -1306,9 +1307,13 @@ class Menu(GalleryMixin, TranslatedFieldsMixin, BaseAttributes): class MenuFiles(FileMixin, BaseAttributes): """Menu files""" + TYPES = ( + ('image', 'Image'), + ('file', 'File') + ) name = models.CharField(_('name'), max_length=255, default='') - type = models.CharField(_('type'), max_length=65, default='') + type = models.CharField(_('type'), choices=TYPES, max_length=65, default='') class Meta: verbose_name = _('menu upload') diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 4bb1a8d4..f861d593 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -2,6 +2,7 @@ import logging from django.conf import settings +from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _ from phonenumber_field.phonenumber import to_python as str_to_phonenumber from rest_framework import serializers @@ -9,24 +10,18 @@ from rest_framework import serializers from comment import models as comment_models from comment.serializers import common as comment_serializers from establishment import models -from establishment.models import Establishment from location.serializers import ( - AddressBaseSerializer, CityBaseSerializer, AddressDetailSerializer, - CityShortSerializer, -) -from location.serializers import ( - EstablishmentWineRegionBaseSerializer, - EstablishmentWineOriginBaseSerializer, + AddressBaseSerializer, AddressDetailSerializer, CityBaseSerializer, + CityShortSerializer, EstablishmentWineOriginBaseSerializer, EstablishmentWineRegionBaseSerializer, ) from main.serializers import AwardSerializer, CurrencySerializer from review.serializers import ReviewShortSerializer from tag.serializers import TagBaseSerializer from timetable.serialziers import ScheduleRUDSerializer from utils import exceptions as utils_exceptions -from utils.serializers import ImageBaseSerializer, CarouselCreateSerializer from utils.serializers import ( + CarouselCreateSerializer, FavoritesCreateSerializer, ImageBaseSerializer, ProjectModelSerializer, TranslatedField, - FavoritesCreateSerializer, ) logger = logging.getLogger(__name__) @@ -77,22 +72,41 @@ class PlateSerializer(ProjectModelSerializer): ] +class MenuFilesSerializers(ProjectModelSerializer): + menu_id = serializers.IntegerField(write_only=True) + + class Meta: + model = models.MenuFiles + fields = [ + 'id', + 'name', + 'type', + 'file', + 'menu_id' + ] + + def create(self, validated_data): + menu_id = validated_data.pop('menu_id') + menu = get_object_or_404(models.Menu, pk=menu_id) + instance = models.MenuFiles.objects.create(**validated_data) + menu.uploads.add(instance) + return instance + + class MenuSerializers(ProjectModelSerializer): name = serializers.CharField() establishment_id = serializers.PrimaryKeyRelatedField(queryset=models.Establishment.objects.all()) establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') - price = serializers.IntegerField(read_only=True, source='establishment.price_level', required=False) - drinks_included = serializers.BooleanField(source='is_drinks_included', required=FavoritesCreateSerializer) + price = serializers.IntegerField(required=False) + drinks_included = serializers.BooleanField(source='is_drinks_included', required=False) schedules = ScheduleRUDSerializer(many=True, allow_null=True, required=False) - uploads = serializers.FileField(allow_null=True, required=False) - category_translated = serializers.CharField(read_only=True) + uploads = MenuFilesSerializers(many=True, read_only=True) class Meta: model = models.Menu fields = [ 'id', 'name', - 'establishment', 'establishment_id', 'establishment_slug', 'price', @@ -100,39 +114,35 @@ class MenuSerializers(ProjectModelSerializer): 'schedules', 'uploads', 'category', - 'category_translated', ] def create(self, validated_data): - print(validated_data, '\n\n\n\n\n') - establishment = models.Establishment.objects.get(pk=validated_data.pop('establishment')) - validated_data['establishment'] = establishment['id'] + validated_data['establishment'] = validated_data.pop('establishment_id') instance = models.Menu.objects.create(**validated_data) return instance -''' -{ - "name": "Menu test", - "establishment_id": 1, - "price": 1, - "drinks_included": true, - "schedules": [], - "uploads": null, - "category": null -} -''' - class MenuRUDSerializers(ProjectModelSerializer): - plates = PlateSerializer(read_only=True, many=True, source='plate_set') + name = serializers.CharField() + establishment_id = serializers.PrimaryKeyRelatedField(read_only=True) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + price = serializers.IntegerField(required=False) + drinks_included = serializers.BooleanField(source='is_drinks_included', required=False) + schedules = ScheduleRUDSerializer(many=True, allow_null=True, required=False) + uploads = MenuFilesSerializers(many=True) class Meta: model = models.Menu fields = [ 'id', + 'name', + 'establishment_id', + 'establishment_slug', + 'price', + 'drinks_included', + 'schedules', + 'uploads', 'category', - 'plates', - 'establishment' ] @@ -566,7 +576,7 @@ class EstablishmentCommentBaseSerializer(comment_serializers.CommentBaseSerializ 'created', 'text', 'mark', - 'nickname', + # 'nickname', 'profile_pic', 'status', 'status_display', @@ -606,7 +616,7 @@ class EstablishmentCommentRUDSerializer(comment_serializers.CommentBaseSerialize 'created', 'text', 'mark', - 'nickname', + # 'nickname', 'profile_pic', ] diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index 6db9d07a..b046cf85 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -37,6 +37,8 @@ urlpatterns = [ # name='menu-dishes-gallery-create-destroy'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), + path('menus/uploads/', views.MenuFilesListCreateView.as_view(), name='menu-files-list'), + path('menus/uploads//', views.MenuFilesRUDView.as_view(), name='menu-files-rud'), path('plates/', views.PlateListCreateView.as_view(), name='plates'), path('plates//', views.PlateRUDView.as_view(), name='plate-rud'), path('social_choice/', views.SocialChoiceListCreateView.as_view(), name='socials_choice'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index b56ebf10..e7e2ad44 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -2,7 +2,7 @@ from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import generics, permissions, response +from rest_framework import generics, permissions from rest_framework.response import Response from account.models import User @@ -136,6 +136,20 @@ class MenuRUDView(generics.RetrieveUpdateDestroyAPIView): permission_classes = [IsWineryReviewer | IsEstablishmentManager] +class MenuFilesListCreateView(generics.ListCreateAPIView): + """Menu files list create view.""" + serializer_class = serializers.MenuFilesSerializers + queryset = models.MenuFiles.objects.all() + permission_classes = [IsWineryReviewer | IsEstablishmentManager] + + +class MenuFilesRUDView(generics.RetrieveDestroyAPIView): + """Menu files RUD view.""" + serializer_class = serializers.MenuFilesSerializers + queryset = models.MenuFiles.objects.all() + permission_classes = [IsWineryReviewer | IsEstablishmentManager] + + class SocialChoiceListCreateView(generics.ListCreateAPIView): """SocialChoice list create view.""" serializer_class = serializers.SocialChoiceSerializers @@ -466,7 +480,6 @@ class MenuDishesRUDView(generics.RetrieveUpdateDestroyAPIView): queryset = models.Menu.objects.dishes().distinct() permission_classes = [IsWineryReviewer | IsEstablishmentManager] - # class MenuGalleryListView(generics.ListAPIView): # """Resource for returning gallery for menu for back-office users.""" # serializer_class = serializers.ImageBaseSerializer From a6db15ed50c2132c8b4ffc21422b7ea81026e537 Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 29 Jan 2020 16:11:53 +0300 Subject: [PATCH 3/5] Added menu dishes --- apps/establishment/models.py | 64 +++++++++------ apps/establishment/serializers/back.py | 99 ++++-------------------- apps/establishment/serializers/common.py | 15 +++- apps/establishment/urls/back.py | 6 +- apps/establishment/views/back.py | 58 +------------- 5 files changed, 74 insertions(+), 168 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 9f5fa7fd..22cd9925 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -6,36 +6,32 @@ from typing import List import elasticsearch_dsl from django.conf import settings -from django.shortcuts import get_object_or_404 from django.contrib.contenttypes import fields as generic from django.contrib.gis.db.models.functions import Distance from django.contrib.gis.geos import Point from django.contrib.gis.measure import Distance as DistanceMeasure from django.contrib.postgres.fields import ArrayField -from django.contrib.postgres.search import TrigramDistance, TrigramSimilarity from django.contrib.postgres.indexes import GinIndex +from django.contrib.postgres.search import TrigramSimilarity from django.core.exceptions import ValidationError -from django.core.validators import MinValueValidator, MaxValueValidator, FileExtensionValidator +from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models -from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q, Prefetch, Sum +from django.db.models import Case, ExpressionWrapper, F, Prefetch, Q, Subquery, When +from django.shortcuts import get_object_or_404 from django.utils import timezone from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField from timezone_field import TimeZoneField from location.models import Address -from timetable.models import Timetable -from location.models import WineOriginAddressMixin from main.models import Award, Currency from review.models import Review from tag.models import Tag +from timetable.models import Timetable from utils.methods import transform_into_readable_str from utils.models import ( - ProjectBaseMixin, TJSONField, URLImageMixin, - TranslatedFieldsMixin, BaseAttributes, GalleryMixin, - IntermediateGalleryModelMixin, HasTagsMixin, - FavoritesMixin, TypeDefaultImageMixin, FileMixin, - ImageMixin, + BaseAttributes, FavoritesMixin, FileMixin, GalleryMixin, HasTagsMixin, IntermediateGalleryModelMixin, + ProjectBaseMixin, TJSONField, TranslatedFieldsMixin, TypeDefaultImageMixin, URLImageMixin, ) @@ -564,8 +560,8 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, verbose_name=_('Facebook URL')) twitter = models.URLField(blank=True, null=True, default=None, max_length=255, verbose_name=_('Twitter URL')) - instagram =models.URLField(blank=True, null=True, default=None, max_length=255, - verbose_name=_('Instagram URL')) + instagram = models.URLField(blank=True, null=True, default=None, max_length=255, + verbose_name=_('Instagram URL')) lafourchette = models.URLField(blank=True, null=True, default=None, max_length=255, verbose_name=_('Lafourchette URL')) guestonline_id = models.PositiveIntegerField(blank=True, verbose_name=_('guestonline id'), @@ -737,7 +733,8 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, now_at_est_tz = datetime.now(tz=self.tz) current_week = now_at_est_tz.weekday() schedule_for_today = self.schedule.filter(weekday=current_week).first() - if schedule_for_today is None or schedule_for_today.opening_time is None or schedule_for_today.ending_time is None: + if schedule_for_today is None or schedule_for_today.opening_time is None or schedule_for_today.ending_time is\ + None: return False time_at_est_tz = now_at_est_tz.time() return schedule_for_today.ending_time > time_at_est_tz > schedule_for_today.opening_time @@ -749,8 +746,10 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, @property def tags_indexing(self): - return [{'id': tag.metadata.id, - 'label': tag.metadata.label} for tag in self.tags.all()] + return [{ + 'id': tag.metadata.id, + 'label': tag.metadata.label + } for tag in self.tags.all()] @property def last_published_review(self): @@ -1244,6 +1243,24 @@ class Plate(TranslatedFieldsMixin, models.Model): verbose_name_plural = _('plates') +class MenuDish(BaseAttributes): + """Dish model.""" + + STR_FIELD_NAME = 'category' + + name = models.CharField(_('name'), max_length=255, default='') + category = TJSONField( + blank=True, null=True, default=None, verbose_name=_('category'), + help_text='{"en-GB":"some text"}') + price = models.IntegerField(blank=True, null=True, default=None, verbose_name=_('price')) + signature = models.BooleanField(_('signature'), default=False) + + class Meta: + verbose_name = _('dish') + verbose_name_plural = _('dishes') + ordering = ('-created',) + + class MenuQuerySet(models.QuerySet): def with_schedule_plates_establishment(self): return self.select_related( @@ -1270,15 +1287,10 @@ class MenuQuerySet(models.QuerySet): return self.filter(category__icontains=value) -class Menu(GalleryMixin, TranslatedFieldsMixin, BaseAttributes): +class Menu(TranslatedFieldsMixin, BaseAttributes): """Menu model.""" - STR_FIELD_NAME = 'category' - name = models.CharField(_('name'), max_length=255, default='') - category = TJSONField( - blank=True, null=True, default=None, verbose_name=_('category'), - help_text='{"en-GB":"some text"}') establishment = models.ForeignKey( 'establishment.Establishment', verbose_name=_('establishment'), on_delete=models.CASCADE) @@ -1294,6 +1306,14 @@ class Menu(GalleryMixin, TranslatedFieldsMixin, BaseAttributes): blank=True, verbose_name=_('Menu files'), ) + dishes = models.ManyToManyField( + to='MenuDish', + blank=True, + verbose_name=_('Menu dishes') + ) + category = TJSONField( + blank=True, null=True, default=None, verbose_name=_('category'), + help_text='{"en-GB":"some text"}') old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index e3adb490..4b92d985 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -1,12 +1,14 @@ from functools import lru_cache from django.db.models import F +from django.shortcuts import get_object_or_404 from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from account.serializers.common import UserShortSerializer from establishment import models, serializers as model_serializers from establishment.models import ContactPhone, EstablishmentEmployee, ContactEmail +from establishment.serializers import MenuDishSerializer from gallery.models import Image from location.models import Address from location.serializers import AddressDetailSerializer, TranslatedField @@ -580,109 +582,36 @@ class EstablishmentEmployeePositionsSerializer(serializers.ModelSerializer): ] -class _PlateSerializer(ProjectModelSerializer): - name_translated = TranslatedField() - - class Meta: - model = models.Plate - fields = [ - 'name_translated', - 'price', - ] - - -class _MenuUploadsSerializer(serializers.Serializer): - id = serializers.IntegerField() - title = serializers.CharField() - original_url = serializers.URLField() - - class MenuDishesSerializer(ProjectModelSerializer): """for dessert, main_course and starter category""" - plates = _PlateSerializer(read_only=True, many=True, source='plate_set', allow_null=True) - category_translated = serializers.CharField(read_only=True) - last_update = serializers.DateTimeField(source='created') - gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True) + establishment_id = serializers.PrimaryKeyRelatedField(read_only=True) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + dishes = MenuDishSerializer(many=True, read_only=True) class Meta: model = models.Menu fields = [ 'id', - 'category', - 'category_translated', - 'establishment', - 'is_drinks_included', - 'schedule', - 'plates', - 'last_update', - 'gallery', + 'establishment_id', + 'establishment_slug', + 'dishes', ] class MenuDishesRUDSerializers(ProjectModelSerializer): """for dessert, main_course and starter category""" - plates = _PlateSerializer(read_only=True, many=True, source='plate_set', allow_null=True) - gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True) + establishment_id = serializers.PrimaryKeyRelatedField(queryset=models.Establishment.objects.all()) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + dishes = MenuDishSerializer(many=True, read_only=True) class Meta: model = models.Menu fields = [ 'id', - 'category', - 'plates', - 'establishment', - 'is_drinks_included', - 'schedule', - 'gallery', + 'establishment_id', + 'establishment_slug', + 'dishes', ] - -# class MenuGallerySerializer(serializers.ModelSerializer): -# """Serializer class for model MenuGallery.""" -# -# class Meta: -# """Meta class""" -# -# model = models.MenuGallery -# fields = [ -# 'id', -# 'is_main', -# ] -# -# @property -# def request_kwargs(self): -# """Get url kwargs from request.""" -# return self.context.get('request').parser_context.get('kwargs') -# -# def create(self, validated_data): -# menu_pk = self.request_kwargs.get('pk') -# image_id = self.request_kwargs.get('image_id') -# qs = models.MenuGallery.objects.filter(image_id=image_id, menu_id=menu_pk) -# instance = qs.first() -# if instance: -# qs.update(**validated_data) -# return instance -# return super().create(validated_data) -# -# def validate(self, attrs): -# """Override validate method.""" -# menu_pk = self.request_kwargs.get('pk') -# image_id = self.request_kwargs.get('image_id') -# -# menu_qs = models.Menu.objects.filter(pk=menu_pk) -# image_qs = Image.objects.filter(id=image_id) -# -# if not menu_qs.exists(): -# raise serializers.ValidationError({'detail': _('Menu not found')}) -# if not image_qs.exists(): -# raise serializers.ValidationError({'detail': _('Image not found')}) -# -# menu = menu_qs.first() -# image = image_qs.first() -# -# attrs['menu'] = menu -# attrs['image'] = image -# -# return attrs diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index f861d593..3c58f476 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -72,6 +72,19 @@ class PlateSerializer(ProjectModelSerializer): ] +class MenuDishSerializer(ProjectModelSerializer): + class Meta: + model = models.MenuDish + fields = [ + 'id', + 'name', + 'category', + 'category_translated', + 'price', + 'signature' + ] + + class MenuFilesSerializers(ProjectModelSerializer): menu_id = serializers.IntegerField(write_only=True) @@ -113,7 +126,6 @@ class MenuSerializers(ProjectModelSerializer): 'drinks_included', 'schedules', 'uploads', - 'category', ] def create(self, validated_data): @@ -142,7 +154,6 @@ class MenuRUDSerializers(ProjectModelSerializer): 'drinks_included', 'schedules', 'uploads', - 'category', ] diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index b046cf85..053d47fd 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -30,13 +30,11 @@ urlpatterns = [ name='note-rud'), path('slug//admin/', views.EstablishmentAdminView.as_view(), name='establishment-admin-list'), - path('menus/dishes/', views.MenuDishesListCreateView.as_view(), name='menu-dishes-list'), + path('menus/dishes/', views.MenuDishesListView.as_view(), name='menu-dishes-list'), path('menus/dishes//', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'), - # path('menus/dishes//gallery/', views.MenuGalleryListView.as_view(), name='menu-dishes-gallery-list'), - # path('menus/dishes//gallery//', views.MenuGalleryCreateDestroyView.as_view(), - # name='menu-dishes-gallery-create-destroy'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), + path('menus/slug//', views.MenuRUDView.as_view(), name='menu-rud'), path('menus/uploads/', views.MenuFilesListCreateView.as_view(), name='menu-files-list'), path('menus/uploads//', views.MenuFilesRUDView.as_view(), name='menu-files-rud'), path('plates/', views.PlateListCreateView.as_view(), name='plates'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index e7e2ad44..d1384b1e 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -466,10 +466,10 @@ class EstablishmentAdminView(generics.ListAPIView): return User.objects.establishment_admin(establishment).distinct() -class MenuDishesListCreateView(generics.ListCreateAPIView): +class MenuDishesListView(generics.ListAPIView): """Menu (dessert, main_course, starter) list create view.""" serializer_class = serializers.MenuDishesSerializer - queryset = models.Menu.objects.with_schedule_plates_establishment().dishes().distinct() + queryset = models.Menu.objects.all() permission_classes = [IsWineryReviewer | IsEstablishmentManager] filter_class = filters.MenuDishesBackFilter @@ -477,57 +477,5 @@ class MenuDishesListCreateView(generics.ListCreateAPIView): class MenuDishesRUDView(generics.RetrieveUpdateDestroyAPIView): """Menu (dessert, main_course, starter) RUD view.""" serializer_class = serializers.MenuDishesRUDSerializers - queryset = models.Menu.objects.dishes().distinct() + queryset = models.Menu.objects.all() permission_classes = [IsWineryReviewer | IsEstablishmentManager] - -# class MenuGalleryListView(generics.ListAPIView): -# """Resource for returning gallery for menu for back-office users.""" -# serializer_class = serializers.ImageBaseSerializer -# permission_classes = [IsWineryReviewer | IsEstablishmentManager] -# queryset = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes() -# -# def get_object(self): -# """Override get_object method.""" -# qs = super(MenuGalleryListView, self).get_queryset() -# menu = get_object_or_404(qs, pk=self.kwargs.get('pk')) -# -# # May raise a permission denied -# # self.check_object_permissions(self.request, menu) -# -# return menu -# -# def get_queryset(self): -# """Override get_queryset method.""" -# return self.get_object().crop_gallery -# -# -# class MenuGalleryCreateDestroyView(CreateDestroyGalleryViewMixin): -# """Resource for a create gallery for menu for back-office users.""" -# serializer_class = serializers.MenuGallerySerializer -# permission_classes = [IsWineryReviewer | IsEstablishmentManager] -# -# def get_queryset(self): -# """Override get_queryset method.""" -# qs = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes() -# return qs -# -# def create(self, request, *args, **kwargs): -# _ = super().create(request, *args, **kwargs) -# news_qs = self.filter_queryset(self.get_queryset()) -# return response.Response( -# data=serializers.MenuDishesRUDSerializers(get_object_or_404(news_qs, pk=kwargs.get('pk'))).data -# ) -# -# def get_object(self): -# """ -# Returns the object the view is displaying. -# """ -# menu_qs = self.filter_queryset(self.get_queryset()) -# -# menu = get_object_or_404(menu_qs, pk=self.kwargs.get('pk')) -# gallery = get_object_or_404(menu.menu_gallery, image_id=self.kwargs.get('image_id')) -# -# # May raise a permission denied -# self.check_object_permissions(self.request, gallery) -# -# return gallery From 19cf1b07d651482c0e210f1cacb5bc19137e551b Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 29 Jan 2020 18:19:28 +0300 Subject: [PATCH 4/5] Added rud finding --- apps/establishment/models.py | 11 ++++--- apps/establishment/serializers/back.py | 27 ++++++++++++++-- apps/establishment/serializers/common.py | 1 - apps/establishment/urls/back.py | 2 ++ apps/establishment/views/back.py | 39 ++++++++++++++++++++---- 5 files changed, 67 insertions(+), 13 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 22cd9925..9999d25a 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -733,7 +733,7 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, now_at_est_tz = datetime.now(tz=self.tz) current_week = now_at_est_tz.weekday() schedule_for_today = self.schedule.filter(weekday=current_week).first() - if schedule_for_today is None or schedule_for_today.opening_time is None or schedule_for_today.ending_time is\ + if schedule_for_today is None or schedule_for_today.opening_time is None or schedule_for_today.ending_time is \ None: return False time_at_est_tz = now_at_est_tz.time() @@ -747,9 +747,9 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, @property def tags_indexing(self): return [{ - 'id': tag.metadata.id, - 'label': tag.metadata.label - } for tag in self.tags.all()] + 'id': tag.metadata.id, + 'label': tag.metadata.label + } for tag in self.tags.all()] @property def last_published_review(self): @@ -1286,6 +1286,9 @@ class MenuQuerySet(models.QuerySet): """Search by category.""" return self.filter(category__icontains=value) + def with_dishes(self): + return self.filter(~Q(dishes=None)) + class Menu(TranslatedFieldsMixin, BaseAttributes): """Menu model.""" diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 4b92d985..85fe8fa2 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -7,7 +7,7 @@ from rest_framework import serializers from account.serializers.common import UserShortSerializer from establishment import models, serializers as model_serializers -from establishment.models import ContactPhone, EstablishmentEmployee, ContactEmail +from establishment.models import ContactEmail, ContactPhone, EstablishmentEmployee from establishment.serializers import MenuDishSerializer from gallery.models import Image from location.models import Address @@ -599,6 +599,30 @@ class MenuDishesSerializer(ProjectModelSerializer): ] +class MenuDishesCreateSerializer(ProjectModelSerializer): + """Menu dishes create serializer""" + + menu_id = serializers.IntegerField(write_only=True) + + class Meta: + model = models.MenuDish + fields = [ + 'id', + 'name', + 'category', + 'price', + 'signature', + 'menu_id' + ] + + def create(self, validated_data): + menu_id = validated_data.pop('menu_id') + menu = get_object_or_404(models.Menu, pk=menu_id) + instance = models.MenuDish.objects.create(**validated_data) + menu.dishes.add(instance) + return instance + + class MenuDishesRUDSerializers(ProjectModelSerializer): """for dessert, main_course and starter category""" @@ -614,4 +638,3 @@ class MenuDishesRUDSerializers(ProjectModelSerializer): 'establishment_slug', 'dishes', ] - diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 3c58f476..3af43e27 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -79,7 +79,6 @@ class MenuDishSerializer(ProjectModelSerializer): 'id', 'name', 'category', - 'category_translated', 'price', 'signature' ] diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index 053d47fd..09c2f2a8 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -31,7 +31,9 @@ urlpatterns = [ path('slug//admin/', views.EstablishmentAdminView.as_view(), name='establishment-admin-list'), path('menus/dishes/', views.MenuDishesListView.as_view(), name='menu-dishes-list'), + path('menus/dishes/create/', views.MenuDishesCreateView.as_view(), name='menu-dishes-create'), path('menus/dishes//', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'), + path('menus/dishes/slug//', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), path('menus/slug//', views.MenuRUDView.as_view(), name='menu-rud'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index d1384b1e..39ede5c3 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -1,5 +1,6 @@ """Establishment app views.""" - +from django.db.models.query_utils import Q +from django.http import Http404 from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend from rest_framework import generics, permissions @@ -14,6 +15,20 @@ from utils.permissions import IsCountryAdmin, IsEstablishmentManager, IsWineryRe from utils.views import CreateDestroyGalleryViewMixin +class MenuRUDMixinViews: + """Menu mixin""" + + def get_object(self): + instance = self.get_queryset().filter( + Q(establishment__slug=self.kwargs.get('slug')) | Q(establishment__id=self.kwargs.get('pk')) + ).first() + + if instance is None: + raise Http404 + + return instance + + class EstablishmentMixinViews: """Establishment mixin.""" @@ -121,7 +136,7 @@ class MenuListCreateView(generics.ListCreateAPIView): permission_classes = [IsWineryReviewer | IsEstablishmentManager] filter_backends = (DjangoFilterBackend,) filterset_fields = ( - 'establishment', + 'establishment__id', 'establishment__slug', ) @@ -129,8 +144,10 @@ class MenuListCreateView(generics.ListCreateAPIView): return super().get_queryset().prefetch_related('establishment') -class MenuRUDView(generics.RetrieveUpdateDestroyAPIView): +class MenuRUDView(MenuRUDMixinViews, generics.RetrieveUpdateDestroyAPIView): """Menu RUD view.""" + + lookup_field = None serializer_class = serializers.MenuRUDSerializers queryset = models.Menu.objects.all() permission_classes = [IsWineryReviewer | IsEstablishmentManager] @@ -469,13 +486,23 @@ class EstablishmentAdminView(generics.ListAPIView): class MenuDishesListView(generics.ListAPIView): """Menu (dessert, main_course, starter) list create view.""" serializer_class = serializers.MenuDishesSerializer - queryset = models.Menu.objects.all() + queryset = models.Menu.objects.with_dishes() permission_classes = [IsWineryReviewer | IsEstablishmentManager] filter_class = filters.MenuDishesBackFilter -class MenuDishesRUDView(generics.RetrieveUpdateDestroyAPIView): +class MenuDishesRUDView(MenuRUDMixinViews, generics.RetrieveUpdateDestroyAPIView): """Menu (dessert, main_course, starter) RUD view.""" + + lookup_field = None serializer_class = serializers.MenuDishesRUDSerializers - queryset = models.Menu.objects.all() + queryset = models.Menu.objects.with_dishes() permission_classes = [IsWineryReviewer | IsEstablishmentManager] + + +class MenuDishesCreateView(generics.CreateAPIView): + """Menu (dessert, main_course, starter) list create view.""" + serializer_class = serializers.MenuDishesCreateSerializer + queryset = models.MenuDish.objects.all() + permission_classes = [IsWineryReviewer | IsEstablishmentManager] + filter_class = filters.MenuDishesBackFilter From e9646acfe1385a0c09841b19bf8f1a2101fe4f4d Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 29 Jan 2020 18:38:15 +0300 Subject: [PATCH 5/5] Add migration --- .../migrations/0086_auto_20200129_1301.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 apps/establishment/migrations/0086_auto_20200129_1301.py diff --git a/apps/establishment/migrations/0086_auto_20200129_1301.py b/apps/establishment/migrations/0086_auto_20200129_1301.py new file mode 100644 index 00000000..99ad4d31 --- /dev/null +++ b/apps/establishment/migrations/0086_auto_20200129_1301.py @@ -0,0 +1,47 @@ +# Generated by Django 2.2.7 on 2020-01-29 13:01 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import utils.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('establishment', '0085_menu_price'), + ] + + operations = [ + migrations.AlterField( + model_name='menufiles', + name='type', + field=models.CharField(choices=[('image', 'Image'), ('file', 'File')], default='', max_length=65, verbose_name='type'), + ), + migrations.CreateModel( + name='MenuDish', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('name', models.CharField(default='', max_length=255, verbose_name='name')), + ('category', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='category')), + ('price', models.IntegerField(blank=True, default=None, null=True, verbose_name='price')), + ('signature', models.BooleanField(default=False, verbose_name='signature')), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menudish_records_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')), + ('modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menudish_records_modified', to=settings.AUTH_USER_MODEL, verbose_name='modified by')), + ], + options={ + 'verbose_name': 'dish', + 'verbose_name_plural': 'dishes', + 'ordering': ('-created',), + }, + ), + migrations.AddField( + model_name='menu', + name='dishes', + field=models.ManyToManyField(blank=True, to='establishment.MenuDish', verbose_name='Menu dishes'), + ), + ]