From e067e8e793eda39c5910411d76c73d7cea50bf4b Mon Sep 17 00:00:00 2001 From: dormantman Date: Tue, 28 Jan 2020 18:29:02 +0300 Subject: [PATCH] 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,