Merge branch 'feature/bo-menus' into 'develop'
Feature/bo menus See merge request gm/gm-backend!247
This commit is contained in:
commit
0e7fd39d37
69
apps/establishment/migrations/0084_auto_20200129_1113.py
Normal file
69
apps/establishment/migrations/0084_auto_20200129_1113.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
18
apps/establishment/migrations/0085_menu_price.py
Normal file
18
apps/establishment/migrations/0085_menu_price.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
47
apps/establishment/migrations/0086_auto_20200129_1301.py
Normal file
47
apps/establishment/migrations/0086_auto_20200129_1301.py
Normal 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'),
|
||||
),
|
||||
]
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user