Merge branch 'feature/bo-menus' into 'develop'

Feature/bo menus

See merge request gm/gm-backend!247
This commit is contained in:
Олег Хаятов 2020-01-29 15:54:44 +00:00
commit 0e7fd39d37
10 changed files with 412 additions and 232 deletions

View File

@ -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'),
),
]

View File

@ -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'),
),
]

View File

@ -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'),
),
]

View File

@ -6,34 +6,33 @@ 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)
from utils.models import (
BaseAttributes, FavoritesMixin, FileMixin, GalleryMixin, HasTagsMixin, IntermediateGalleryModelMixin,
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin, TypeDefaultImageMixin, URLImageMixin,
)
# todo: establishment type&subtypes check
@ -561,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'),
@ -734,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
@ -746,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):
@ -1241,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(
@ -1266,25 +1286,38 @@ 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(GalleryMixin, TranslatedFieldsMixin, BaseAttributes):
class Menu(TranslatedFieldsMixin, BaseAttributes):
"""Menu model."""
STR_FIELD_NAME = 'category'
category = TJSONField(
blank=True, null=True, default=None, verbose_name=_('category'),
help_text='{"en-GB":"some text"}')
name = models.CharField(_('name'), max_length=255, default='')
establishment = models.ForeignKey(
'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,
verbose_name=_('Establishment schedule'),
related_name='menus',
verbose_name=_('Menu schedule'),
)
uploads = models.ManyToManyField(
to='MenuFiles',
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)
objects = MenuQuerySet.as_manager()
@ -1295,43 +1328,15 @@ 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"""
TYPES = (
('image', 'Image'),
('file', 'File')
)
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'), choices=TYPES, max_length=65, default='')
class Meta:
verbose_name = _('menu upload')

View File

@ -2,12 +2,14 @@ from functools import lru_cache
from pprint import pprint
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.models import ContactEmail, ContactPhone, EstablishmentEmployee
from establishment.serializers import MenuDishSerializer
from gallery.models import Image
from location.models import Address
from location.serializers import AddressDetailSerializer, TranslatedField
@ -584,109 +586,59 @@ 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 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"""
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

View File

@ -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,18 +10,19 @@ 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 location.serializers import (
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 (ProjectModelSerializer, TranslatedField,
FavoritesCreateSerializer)
from utils.serializers import (
CarouselCreateSerializer, FavoritesCreateSerializer, ImageBaseSerializer,
ProjectModelSerializer, TranslatedField,
)
logger = logging.getLogger(__name__)
@ -70,31 +72,87 @@ class PlateSerializer(ProjectModelSerializer):
]
class MenuSerializers(ProjectModelSerializer):
plates = PlateSerializer(read_only=True, many=True, source='plate_set')
category_translated = serializers.CharField(read_only=True)
class MenuDishSerializer(ProjectModelSerializer):
class Meta:
model = models.Menu
model = models.MenuDish
fields = [
'id',
'name',
'category',
'category_translated',
'plates',
'establishment'
'price',
'signature'
]
class MenuRUDSerializers(ProjectModelSerializer):
plates = PlateSerializer(read_only=True, many=True, source='plate_set')
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(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, read_only=True)
class Meta:
model = models.Menu
fields = [
'id',
'category',
'plates',
'establishment'
'name',
'establishment_id',
'establishment_slug',
'price',
'drinks_included',
'schedules',
'uploads',
]
def create(self, validated_data):
validated_data['establishment'] = validated_data.pop('establishment_id')
instance = models.Menu.objects.create(**validated_data)
return instance
class MenuRUDSerializers(ProjectModelSerializer):
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',
]
@ -528,7 +586,7 @@ class EstablishmentCommentBaseSerializer(comment_serializers.CommentBaseSerializ
'created',
'text',
'mark',
'nickname',
# 'nickname',
'profile_pic',
'status',
'status_display',
@ -568,7 +626,7 @@ class EstablishmentCommentRUDSerializer(comment_serializers.CommentBaseSerialize
'created',
'text',
'mark',
'nickname',
# 'nickname',
'profile_pic',
]

View File

@ -30,13 +30,15 @@ urlpatterns = [
name='note-rud'),
path('slug/<slug: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/create/', views.MenuDishesCreateView.as_view(), name='menu-dishes-create'),
path('menus/dishes/<int:pk>/', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'),
path('menus/dishes/<int:pk>/gallery/', views.MenuGalleryListView.as_view(), name='menu-dishes-gallery-list'),
path('menus/dishes/<int:pk>/gallery/<int:image_id>/', views.MenuGalleryCreateDestroyView.as_view(),
name='menu-dishes-gallery-create-destroy'),
path('menus/dishes/slug/<slug:slug>/', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'),
path('menus/', views.MenuListCreateView.as_view(), name='menu-list'),
path('menus/<int:pk>/', views.MenuRUDView.as_view(), name='menu-rud'),
path('menus/slug/<slug:slug>/', views.MenuRUDView.as_view(), name='menu-rud'),
path('menus/uploads/', views.MenuFilesListCreateView.as_view(), name='menu-files-list'),
path('menus/uploads/<int:pk>/', views.MenuFilesRUDView.as_view(), name='menu-files-rud'),
path('plates/', views.PlateListCreateView.as_view(), name='plates'),
path('plates/<int:pk>/', views.PlateRUDView.as_view(), name='plate-rud'),
path('social_choice/', views.SocialChoiceListCreateView.as_view(), name='socials_choice'),

View File

@ -1,8 +1,9 @@
"""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, response
from rest_framework import generics, permissions
from rest_framework.response import Response
from account.models import User
@ -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,18 +136,37 @@ class MenuListCreateView(generics.ListCreateAPIView):
permission_classes = [IsWineryReviewer | IsEstablishmentManager]
filter_backends = (DjangoFilterBackend,)
filterset_fields = (
'establishment',
'establishment__id',
'establishment__slug',
)
def get_queryset(self):
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]
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
@ -449,69 +483,26 @@ 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.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.dishes().distinct()
queryset = models.Menu.objects.with_dishes()
permission_classes = [IsWineryReviewer | IsEstablishmentManager]
class MenuGalleryListView(generics.ListAPIView):
"""Resource for returning gallery for menu for back-office users."""
serializer_class = serializers.ImageBaseSerializer
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]
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
filter_class = filters.MenuDishesBackFilter

View File

@ -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()

View File

@ -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,