refactored galleries, added transfer script
This commit is contained in:
parent
6c82af1940
commit
a8b3c9aa63
17
apps/advertisement/migrations/0008_auto_20191117_1117.py
Normal file
17
apps/advertisement/migrations/0008_auto_20191117_1117.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-17 11:17
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('advertisement', '0007_auto_20191115_0750'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='advertisement',
|
||||
options={'verbose_name': 'Advertisement', 'verbose_name_plural': 'Advertisements'},
|
||||
),
|
||||
]
|
||||
|
|
@ -32,6 +32,12 @@ class ContactPhoneInline(admin.TabularInline):
|
|||
extra = 0
|
||||
|
||||
|
||||
class GalleryImageInline(admin.TabularInline):
|
||||
"""Gallery image inline admin."""
|
||||
model = models.EstablishmentGallery
|
||||
extra = 0
|
||||
|
||||
|
||||
class ContactEmailInline(admin.TabularInline):
|
||||
"""Contact email inline admin."""
|
||||
model = models.ContactEmail
|
||||
|
|
@ -59,6 +65,7 @@ class EstablishmentAdmin(BaseModelAdminMixin, admin.ModelAdmin):
|
|||
list_display = ['id', '__str__', 'image_tag', ]
|
||||
search_fields = ['id', 'name', 'index_name', 'slug']
|
||||
list_filter = ['public_mark', 'toque_number']
|
||||
inlines = [GalleryImageInline, ]
|
||||
|
||||
# inlines = [
|
||||
# AwardInline, ContactPhoneInline, ContactEmailInline,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from establishment.models import Establishment, EstablishmentGallery
|
||||
from gallery.models import Image
|
||||
import requests
|
||||
from rest_framework import status
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Fill establishment gallery from existing images'
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
count = 0
|
||||
not_valid_link_counter = 0
|
||||
not_valid_urls = []
|
||||
|
||||
cdn_prefix = 'https://1dc3f33f6d-3.optimicdn.com/gaultmillau.com/'
|
||||
establishments = Establishment.objects.exclude(image_url__isnull=True) \
|
||||
.exclude(preview_image_url__isnull=True)
|
||||
for establishment in establishments:
|
||||
image_url = establishment.image_url.rstrip()
|
||||
relative_image_path = image_url[len(cdn_prefix):]
|
||||
|
||||
response = requests.head(image_url, allow_redirects=True)
|
||||
if response.status_code != status.HTTP_200_OK:
|
||||
not_valid_link_counter += 1
|
||||
not_valid_urls.append(image_url)
|
||||
|
||||
image, image_created = Image.objects.get_or_create(
|
||||
orientation=Image.HORIZONTAL,
|
||||
title=f'{establishment.name} - {relative_image_path}',
|
||||
image=relative_image_path)
|
||||
gallery, _ = EstablishmentGallery.objects.get_or_create(establishment=establishment,
|
||||
image=image,
|
||||
is_main=True)
|
||||
if image_created:
|
||||
count += 1
|
||||
|
||||
self.stdout.write(self.style.WARNING(f'Created/updated {count} objects.\n'
|
||||
f'Not valid link counter: {not_valid_link_counter}\n'
|
||||
f'List of non valid image url: {not_valid_urls}'))
|
||||
38
apps/establishment/migrations/0062_auto_20191117_1117.py
Normal file
38
apps/establishment/migrations/0062_auto_20191117_1117.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-17 11:17
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gallery', '0006_merge_20191027_1758'),
|
||||
('establishment', '0061_auto_20191114_0550'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='establishmentnote',
|
||||
options={'verbose_name': 'establishment note', 'verbose_name_plural': 'establishment notes'},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EstablishmentGallery',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('is_main', models.BooleanField(default=False, verbose_name='Is the main image')),
|
||||
('establishment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='establishment_gallery', to='establishment.Establishment', verbose_name='establishment')),
|
||||
('image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='establishment_gallery', to='gallery.Image', verbose_name='image')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'establishment gallery',
|
||||
'verbose_name_plural': 'establishment galleries',
|
||||
'unique_together': {('establishment', 'is_main'), ('establishment', 'image')},
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='establishment',
|
||||
name='gallery',
|
||||
field=models.ManyToManyField(through='establishment.EstablishmentGallery', to='gallery.Image'),
|
||||
),
|
||||
]
|
||||
|
|
@ -22,7 +22,8 @@ from location.models import Address
|
|||
from main.models import Award, Currency
|
||||
from review.models import Review
|
||||
from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin,
|
||||
TranslatedFieldsMixin, BaseAttributes)
|
||||
TranslatedFieldsMixin, BaseAttributes, GalleryModelMixin,
|
||||
IntermediateGalleryModelMixin)
|
||||
|
||||
|
||||
# todo: establishment type&subtypes check
|
||||
|
|
@ -316,9 +317,11 @@ class EstablishmentQuerySet(models.QuerySet):
|
|||
return self.exclude(address__city__country__in=countries)
|
||||
|
||||
|
||||
class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
|
||||
class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
|
||||
"""Establishment model."""
|
||||
|
||||
# todo: delete image URL fields after moving on gallery
|
||||
|
||||
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
|
||||
name = models.CharField(_('name'), max_length=255, default='')
|
||||
transliterated_name = models.CharField(default='', max_length=255,
|
||||
|
|
@ -376,6 +379,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
|
|||
related_name='establishments',
|
||||
blank=True, default=None,
|
||||
verbose_name=_('Collections'))
|
||||
gallery = models.ManyToManyField('gallery.Image', through='EstablishmentGallery')
|
||||
preview_image_url = models.URLField(verbose_name=_('Preview image URL path'), max_length=255,
|
||||
blank=True, null=True, default=None)
|
||||
slug = models.SlugField(unique=True, max_length=255, null=True,
|
||||
|
|
@ -544,6 +548,12 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
|
|||
"""Return list products with type wine"""
|
||||
return self.products.wines()
|
||||
|
||||
@property
|
||||
def main_image(self):
|
||||
qs = self.establishment_gallery.main_image()
|
||||
if qs.exists():
|
||||
return qs.first().image
|
||||
|
||||
|
||||
class EstablishmentNoteQuerySet(models.QuerySet):
|
||||
"""QuerySet for model EstablishmentNote."""
|
||||
|
|
@ -565,8 +575,26 @@ class EstablishmentNote(ProjectBaseMixin):
|
|||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
verbose_name_plural = _('product note')
|
||||
verbose_name = _('product notes')
|
||||
verbose_name_plural = _('establishment notes')
|
||||
verbose_name = _('establishment note')
|
||||
|
||||
|
||||
class EstablishmentGallery(IntermediateGalleryModelMixin):
|
||||
|
||||
establishment = models.ForeignKey(Establishment, null=True,
|
||||
related_name='establishment_gallery',
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('establishment'))
|
||||
image = models.ForeignKey('gallery.Image', null=True,
|
||||
related_name='establishment_gallery',
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('image'))
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
verbose_name = _('establishment gallery')
|
||||
verbose_name_plural = _('establishment galleries')
|
||||
unique_together = (('establishment', 'is_main'), ('establishment', 'image'))
|
||||
|
||||
|
||||
class Position(BaseAttributes, TranslatedFieldsMixin):
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ from location.serializers import AddressDetailSerializer
|
|||
from main.models import Currency
|
||||
from utils.decorators import with_base_attributes
|
||||
from utils.serializers import TimeZoneChoiceField
|
||||
from gallery.models import Image
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class EstablishmentListCreateSerializer(EstablishmentBaseSerializer):
|
||||
|
|
@ -160,3 +162,45 @@ class EmployeeBackSerializers(serializers.ModelSerializer):
|
|||
'user',
|
||||
'name'
|
||||
]
|
||||
|
||||
|
||||
class EstablishmentBackOfficeGallerySerializer(serializers.ModelSerializer):
|
||||
"""Serializer class for model EstablishmentGallery."""
|
||||
|
||||
class Meta:
|
||||
"""Meta class"""
|
||||
|
||||
model = models.EstablishmentGallery
|
||||
fields = [
|
||||
'id',
|
||||
'is_main',
|
||||
]
|
||||
|
||||
def get_request_kwargs(self):
|
||||
"""Get url kwargs from request."""
|
||||
return self.context.get('request').parser_context.get('kwargs')
|
||||
|
||||
def validate(self, attrs):
|
||||
"""Override validate method."""
|
||||
establishment_pk = self.get_request_kwargs().get('pk')
|
||||
image_id = self.get_request_kwargs().get('image_id')
|
||||
|
||||
establishment_qs = models.Establishment.objects.filter(pk=establishment_pk)
|
||||
image_qs = Image.objects.filter(id=image_id)
|
||||
|
||||
if not establishment_qs.exists():
|
||||
raise serializers.ValidationError({'detail': _('Establishment not found')})
|
||||
|
||||
if not image_qs.exists():
|
||||
raise serializers.ValidationError({'detail': _('Image not found')})
|
||||
|
||||
establishment = establishment_qs.first()
|
||||
image = image_qs.first()
|
||||
|
||||
if image in establishment.gallery.all():
|
||||
raise serializers.ValidationError({'detail': _('Image is already added.')})
|
||||
|
||||
attrs['establishment'] = establishment
|
||||
attrs['image'] = image
|
||||
|
||||
return attrs
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from utils import exceptions as utils_exceptions
|
|||
from utils.serializers import (ProjectModelSerializer, TranslatedField,
|
||||
FavoritesCreateSerializer)
|
||||
from review.serializers import ReviewShortSerializer
|
||||
from utils.serializers import ImageBaseSerializer
|
||||
|
||||
|
||||
class ContactPhonesSerializer(serializers.ModelSerializer):
|
||||
|
|
@ -206,13 +207,18 @@ class EstablishmentProductSerializer(EstablishmentShortSerializer):
|
|||
class EstablishmentBaseSerializer(ProjectModelSerializer):
|
||||
"""Base serializer for Establishment model."""
|
||||
|
||||
preview_image = serializers.URLField(source='preview_image_url')
|
||||
address = AddressBaseSerializer()
|
||||
in_favorites = serializers.BooleanField(allow_null=True)
|
||||
tags = TagBaseSerializer(read_only=True, many=True)
|
||||
currency = CurrencySerializer()
|
||||
type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
|
||||
subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes')
|
||||
image = serializers.URLField(source='image_url', read_only=True)
|
||||
preview_image = serializers.URLField(source='preview_image_url',
|
||||
allow_null=True,
|
||||
read_only=True)
|
||||
|
||||
new_image = ImageBaseSerializer(source='crop_main_image', allow_null=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
|
@ -227,13 +233,15 @@ class EstablishmentBaseSerializer(ProjectModelSerializer):
|
|||
'toque_number',
|
||||
'public_mark',
|
||||
'slug',
|
||||
'preview_image',
|
||||
'in_favorites',
|
||||
'address',
|
||||
'tags',
|
||||
'currency',
|
||||
'type',
|
||||
'subtypes',
|
||||
'image',
|
||||
'preview_image',
|
||||
'new_image',
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -272,7 +280,6 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
|
|||
"""Serializer for Establishment model."""
|
||||
|
||||
description_translated = TranslatedField()
|
||||
image = serializers.URLField(source='image_url')
|
||||
awards = AwardSerializer(many=True)
|
||||
schedule = ScheduleRUDSerializer(many=True, allow_null=True)
|
||||
phones = ContactPhonesSerializer(read_only=True, many=True)
|
||||
|
|
@ -288,6 +295,7 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
|
|||
range_price_menu = RangePriceSerializer(read_only=True)
|
||||
range_price_carte = RangePriceSerializer(read_only=True)
|
||||
vintage_year = serializers.ReadOnlyField()
|
||||
gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True)
|
||||
|
||||
class Meta(EstablishmentBaseSerializer.Meta):
|
||||
"""Meta class."""
|
||||
|
|
@ -313,6 +321,7 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
|
|||
'range_price_carte',
|
||||
'transportation',
|
||||
'vintage_year',
|
||||
'gallery',
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -379,4 +388,3 @@ class EstablishmentFavoritesCreateSerializer(FavoritesCreateSerializer):
|
|||
'content_object': validated_data.pop('establishment')
|
||||
})
|
||||
return super().create(validated_data)
|
||||
|
||||
|
|
|
|||
|
|
@ -31,4 +31,11 @@ urlpatterns = [
|
|||
path('types/<int:pk>/', views.EstablishmentTypeRUDView.as_view(), name='type-rud'),
|
||||
path('subtypes/', views.EstablishmentSubtypeListCreateView.as_view(), name='subtype-list'),
|
||||
path('subtypes/<int:pk>/', views.EstablishmentSubtypeRUDView.as_view(), name='subtype-rud'),
|
||||
|
||||
# gallery
|
||||
path('<int:pk>/gallery/', views.EstablishmentBackOfficeGalleryListView.as_view(),
|
||||
name='gallery-list'),
|
||||
path('<int:pk>/gallery/<int:image_id>/',
|
||||
views.EstablishmentBackOfficeGalleryCreateDestroyView.as_view(),
|
||||
name='gallery-create-destroy'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import generics, permissions
|
||||
|
||||
from utils.permissions import IsCountryAdmin, IsEstablishmentManager
|
||||
from establishment import filters, models, serializers
|
||||
from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer
|
||||
from utils.permissions import IsCountryAdmin, IsEstablishmentManager
|
||||
from utils.views import CreateDestroyGalleryViewMixin
|
||||
|
||||
|
||||
class EstablishmentMixinViews:
|
||||
|
|
@ -184,3 +185,43 @@ class EstablishmentSubtypeRUDView(generics.RetrieveUpdateDestroyAPIView):
|
|||
"""Establishment subtype retrieve/update/destroy view."""
|
||||
serializer_class = serializers.EstablishmentSubTypeBaseSerializer
|
||||
queryset = models.EstablishmentSubType.objects.all()
|
||||
|
||||
|
||||
class EstablishmentBackOfficeGalleryCreateDestroyView(EstablishmentMixinViews,
|
||||
CreateDestroyGalleryViewMixin):
|
||||
"""Resource for a create|destroy gallery for product for back-office users."""
|
||||
serializer_class = serializers.EstablishmentBackOfficeGallerySerializer
|
||||
|
||||
def get_object(self):
|
||||
"""
|
||||
Returns the object the view is displaying.
|
||||
"""
|
||||
establishment_qs = self.filter_queryset(self.get_queryset())
|
||||
|
||||
establishment = get_object_or_404(establishment_qs, pk=self.kwargs['pk'])
|
||||
gallery = get_object_or_404(establishment.establishment_gallery, image_id=self.kwargs['image_id'])
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, gallery)
|
||||
|
||||
return gallery
|
||||
|
||||
|
||||
class EstablishmentBackOfficeGalleryListView(EstablishmentMixinViews,
|
||||
generics.ListAPIView):
|
||||
"""Resource for returning gallery for establishment for back-office users."""
|
||||
serializer_class = serializers.ImageBaseSerializer
|
||||
|
||||
def get_object(self):
|
||||
"""Override get_object method."""
|
||||
qs = super(EstablishmentBackOfficeGalleryListView, self).get_queryset()
|
||||
establishment = get_object_or_404(qs, pk=self.kwargs['pk'])
|
||||
|
||||
# May raise a permission denied
|
||||
self.check_object_permissions(self.request, establishment)
|
||||
|
||||
return establishment
|
||||
|
||||
def get_queryset(self):
|
||||
"""Override get_queryset method."""
|
||||
return self.get_object().crop_gallery
|
||||
|
|
|
|||
|
|
@ -24,12 +24,19 @@ def send_email_action(modeladmin, request, queryset):
|
|||
send_email_action.short_description = "Send the selected news by email"
|
||||
|
||||
|
||||
class NewsGalleryInline(admin.TabularInline):
|
||||
"""News gallery inline."""
|
||||
model = models.NewsGallery
|
||||
extra = 0
|
||||
|
||||
|
||||
@admin.register(models.News)
|
||||
class NewsAdmin(BaseModelAdminMixin, admin.ModelAdmin):
|
||||
"""News admin."""
|
||||
raw_id_fields = ('address',)
|
||||
actions = [send_email_action]
|
||||
raw_id_fields = ('news_type', 'address', 'country')
|
||||
inlines = [NewsGalleryInline, ]
|
||||
|
||||
|
||||
@admin.register(models.NewsGallery)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ from django.utils.translation import gettext_lazy as _
|
|||
from rest_framework.reverse import reverse
|
||||
|
||||
from rating.models import Rating, ViewCount
|
||||
from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin, ProjectBaseMixin
|
||||
from utils.models import (BaseAttributes, TJSONField, TranslatedFieldsMixin,
|
||||
ProjectBaseMixin, GalleryModelMixin, IntermediateGalleryModelMixin)
|
||||
from utils.querysets import TranslationQuerysetMixin
|
||||
|
||||
|
||||
|
|
@ -124,7 +125,7 @@ class NewsQuerySet(TranslationQuerysetMixin):
|
|||
)
|
||||
|
||||
|
||||
class News(BaseAttributes, TranslatedFieldsMixin):
|
||||
class News(GalleryModelMixin, BaseAttributes, TranslatedFieldsMixin):
|
||||
"""News model."""
|
||||
|
||||
STR_FIELD_NAME = 'title'
|
||||
|
|
@ -248,15 +249,8 @@ class News(BaseAttributes, TranslatedFieldsMixin):
|
|||
return count_value
|
||||
|
||||
|
||||
class NewsGalleryQuerySet(models.QuerySet):
|
||||
"""QuerySet for model News"""
|
||||
class NewsGallery(IntermediateGalleryModelMixin):
|
||||
|
||||
def main_image(self):
|
||||
"""Return objects with flag is_main is True"""
|
||||
return self.filter(is_main=True)
|
||||
|
||||
|
||||
class NewsGallery(models.Model):
|
||||
news = models.ForeignKey(News, null=True,
|
||||
related_name='news_gallery',
|
||||
on_delete=models.CASCADE,
|
||||
|
|
@ -265,10 +259,6 @@ class NewsGallery(models.Model):
|
|||
related_name='news_gallery',
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('gallery'))
|
||||
is_main = models.BooleanField(default=False,
|
||||
verbose_name=_('Is the main image'))
|
||||
|
||||
objects = NewsGalleryQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
"""NewsGallery meta class."""
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ from location.serializers import CountrySimpleSerializer, AddressBaseSerializer
|
|||
from news import models
|
||||
from tag.serializers import TagBaseSerializer
|
||||
from utils import exceptions as utils_exceptions
|
||||
from utils.serializers import TranslatedField, ProjectModelSerializer, FavoritesCreateSerializer
|
||||
from utils.serializers import (TranslatedField, ProjectModelSerializer,
|
||||
FavoritesCreateSerializer, ImageBaseSerializer)
|
||||
|
||||
|
||||
class AgendaSerializer(ProjectModelSerializer):
|
||||
|
|
@ -47,78 +48,6 @@ class NewsBannerSerializer(ProjectModelSerializer):
|
|||
)
|
||||
|
||||
|
||||
class CropImageSerializer(serializers.Serializer):
|
||||
"""Serializer for crop images for News object."""
|
||||
|
||||
preview_url = serializers.SerializerMethodField()
|
||||
promo_horizontal_web_url = serializers.SerializerMethodField()
|
||||
promo_horizontal_mobile_url = serializers.SerializerMethodField()
|
||||
tile_horizontal_web_url = serializers.SerializerMethodField()
|
||||
tile_horizontal_mobile_url = serializers.SerializerMethodField()
|
||||
tile_vertical_web_url = serializers.SerializerMethodField()
|
||||
highlight_vertical_web_url = serializers.SerializerMethodField()
|
||||
editor_web_url = serializers.SerializerMethodField()
|
||||
editor_mobile_url = serializers.SerializerMethodField()
|
||||
|
||||
def get_preview_url(self, obj):
|
||||
"""Get crop preview."""
|
||||
return obj.instance.get_image_url('news_preview')
|
||||
|
||||
def get_promo_horizontal_web_url(self, obj):
|
||||
"""Get crop promo_horizontal_web."""
|
||||
return obj.instance.get_image_url('news_promo_horizontal_web')
|
||||
|
||||
def get_promo_horizontal_mobile_url(self, obj):
|
||||
"""Get crop promo_horizontal_mobile."""
|
||||
return obj.instance.get_image_url('news_promo_horizontal_mobile')
|
||||
|
||||
def get_tile_horizontal_web_url(self, obj):
|
||||
"""Get crop tile_horizontal_web."""
|
||||
return obj.instance.get_image_url('news_tile_horizontal_web')
|
||||
|
||||
def get_tile_horizontal_mobile_url(self, obj):
|
||||
"""Get crop tile_horizontal_mobile."""
|
||||
return obj.instance.get_image_url('news_tile_horizontal_mobile')
|
||||
|
||||
def get_tile_vertical_web_url(self, obj):
|
||||
"""Get crop tile_vertical_web."""
|
||||
return obj.instance.get_image_url('news_tile_vertical_web')
|
||||
|
||||
def get_highlight_vertical_web_url(self, obj):
|
||||
"""Get crop highlight_vertical_web."""
|
||||
return obj.instance.get_image_url('news_highlight_vertical_web')
|
||||
|
||||
def get_editor_web_url(self, obj):
|
||||
"""Get crop editor_web."""
|
||||
return obj.instance.get_image_url('news_editor_web')
|
||||
|
||||
def get_editor_mobile_url(self, obj):
|
||||
"""Get crop editor_mobile."""
|
||||
return obj.instance.get_image_url('news_editor_mobile')
|
||||
|
||||
|
||||
class NewsImageSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for returning crop images of news image."""
|
||||
|
||||
orientation_display = serializers.CharField(source='get_orientation_display',
|
||||
read_only=True)
|
||||
original_url = serializers.URLField(source='image.url')
|
||||
auto_crop_images = CropImageSerializer(source='image', allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = Image
|
||||
fields = [
|
||||
'id',
|
||||
'title',
|
||||
'orientation_display',
|
||||
'original_url',
|
||||
'auto_crop_images',
|
||||
]
|
||||
extra_kwargs = {
|
||||
'orientation': {'write_only': True}
|
||||
}
|
||||
|
||||
|
||||
class NewsTypeSerializer(serializers.ModelSerializer):
|
||||
"""News type serializer."""
|
||||
|
||||
|
|
@ -170,7 +99,7 @@ class NewsSimilarListSerializer(NewsBaseSerializer):
|
|||
class NewsListSerializer(NewsBaseSerializer):
|
||||
"""List serializer for News model."""
|
||||
|
||||
image = NewsImageSerializer(source='main_image', allow_null=True)
|
||||
image = ImageBaseSerializer(source='crop_main_image', allow_null=True)
|
||||
|
||||
class Meta(NewsBaseSerializer.Meta):
|
||||
"""Meta class."""
|
||||
|
|
@ -188,7 +117,7 @@ class NewsDetailSerializer(NewsBaseSerializer):
|
|||
author = UserBaseSerializer(source='created_by', read_only=True)
|
||||
state_display = serializers.CharField(source='get_state_display',
|
||||
read_only=True)
|
||||
gallery = NewsImageSerializer(read_only=True, many=True)
|
||||
gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True)
|
||||
|
||||
class Meta(NewsBaseSerializer.Meta):
|
||||
"""Meta class."""
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
"""News app views."""
|
||||
from django.conf import settings
|
||||
from django.db.transaction import on_commit
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import generics, permissions, status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import generics, permissions
|
||||
|
||||
from gallery.tasks import delete_image
|
||||
from news import filters, models, serializers
|
||||
from rating.tasks import add_rating
|
||||
from utils.permissions import IsCountryAdmin, IsContentPageManager
|
||||
from utils.views import CreateDestroyGalleryViewMixin
|
||||
from utils.serializers import ImageBaseSerializer
|
||||
|
||||
|
||||
class NewsMixinView:
|
||||
|
|
@ -118,9 +116,10 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView,
|
|||
return gallery
|
||||
|
||||
|
||||
class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIView):
|
||||
class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView,
|
||||
generics.ListAPIView):
|
||||
"""Resource for returning gallery for news for back-office users."""
|
||||
serializer_class = serializers.NewsImageSerializer
|
||||
serializer_class = ImageBaseSerializer
|
||||
|
||||
def get_object(self):
|
||||
"""Override get_object method."""
|
||||
|
|
@ -134,7 +133,7 @@ class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIVie
|
|||
|
||||
def get_queryset(self):
|
||||
"""Override get_queryset method."""
|
||||
return self.get_object().gallery.all()
|
||||
return self.get_object().crop_gallery
|
||||
|
||||
|
||||
class NewsBackOfficeRUDView(NewsBackOfficeMixinView,
|
||||
|
|
|
|||
|
|
@ -4,22 +4,23 @@ from utils.admin import BaseModelAdminMixin
|
|||
from .models import Product, ProductType, ProductSubType, ProductGallery, Unit
|
||||
|
||||
|
||||
class ProductGalleryInline(admin.TabularInline):
|
||||
"""Product gallery inline."""
|
||||
model = ProductGallery
|
||||
extra = 0
|
||||
|
||||
|
||||
@admin.register(Product)
|
||||
class ProductAdmin(BaseModelAdminMixin, admin.ModelAdmin):
|
||||
"""Admin page for model Product."""
|
||||
search_fields = ('name', )
|
||||
list_filter = ('available', 'product_type')
|
||||
list_display = ('id', '__str__', 'get_category_display', 'product_type')
|
||||
inlines = [ProductGalleryInline, ]
|
||||
raw_id_fields = ('subtypes', 'classifications', 'standards',
|
||||
'tags', 'gallery', 'establishment',)
|
||||
|
||||
|
||||
@admin.register(ProductGallery)
|
||||
class ProductGalleryAdmin(admin.ModelAdmin):
|
||||
"""Admin page for model ProductGallery."""
|
||||
raw_id_fields = ('product', 'image', )
|
||||
|
||||
|
||||
@admin.register(ProductType)
|
||||
class ProductTypeAdmin(admin.ModelAdmin):
|
||||
"""Admin page for model ProductType."""
|
||||
|
|
|
|||
19
apps/product/migrations/0014_auto_20191117_1117.py
Normal file
19
apps/product/migrations/0014_auto_20191117_1117.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-17 11:17
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('product', '0013_auto_20191113_1512'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='productgallery',
|
||||
name='image',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_gallery', to='gallery.Image', verbose_name='image'),
|
||||
),
|
||||
]
|
||||
|
|
@ -8,7 +8,8 @@ from django.utils.translation import gettext_lazy as _
|
|||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
|
||||
from utils.models import (BaseAttributes, ProjectBaseMixin,
|
||||
TranslatedFieldsMixin, TJSONField)
|
||||
TranslatedFieldsMixin, TJSONField,
|
||||
GalleryModelMixin, IntermediateGalleryModelMixin)
|
||||
|
||||
|
||||
class ProductType(TranslatedFieldsMixin, ProjectBaseMixin):
|
||||
|
|
@ -125,7 +126,7 @@ class ProductQuerySet(models.QuerySet):
|
|||
)
|
||||
|
||||
|
||||
class Product(TranslatedFieldsMixin, BaseAttributes):
|
||||
class Product(GalleryModelMixin, TranslatedFieldsMixin, BaseAttributes):
|
||||
"""Product models."""
|
||||
|
||||
EARLIEST_VINTAGE_YEAR = 1700
|
||||
|
|
@ -222,16 +223,6 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
|
|||
"""Override str dunder method."""
|
||||
return f'{self.name}'
|
||||
|
||||
def clean_fields(self, exclude=None):
|
||||
super().clean_fields(exclude=exclude)
|
||||
if self.product_type.index_name == ProductType.WINE and not self.wine_region:
|
||||
raise ValidationError(_('wine_region field must be specified.'))
|
||||
if not self.product_type.index_name == ProductType.WINE and self.wine_region:
|
||||
raise ValidationError(_('wine_region field must not be specified.'))
|
||||
# if (self.wine_region and self.wine_appellation) and \
|
||||
# self.wine_appellation not in self.wine_region.appellations.all():
|
||||
# raise ValidationError(_('Wine appellation not exists in wine region.'))
|
||||
|
||||
@property
|
||||
def product_type_translated_name(self):
|
||||
"""Get translated name of product type."""
|
||||
|
|
@ -254,20 +245,6 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
|
|||
def bottles_produced(self):
|
||||
return self.tags.filter(category__index_name='bottles-produced')
|
||||
|
||||
@property
|
||||
def main_image(self):
|
||||
qs = ProductGallery.objects.filter(product=self, is_main=True)
|
||||
if qs.exists():
|
||||
return qs.first().image
|
||||
|
||||
@property
|
||||
def main_image_url(self):
|
||||
return self.main_image.image if self.main_image else None
|
||||
|
||||
@property
|
||||
def preview_main_image_url(self):
|
||||
return self.main_image.get_image_url('product_preview') if self.main_image else None
|
||||
|
||||
@property
|
||||
def related_tags(self):
|
||||
return self.tags.exclude(
|
||||
|
|
@ -282,6 +259,21 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
|
|||
name = f'{self.establishment.name} - ' + name
|
||||
return name
|
||||
|
||||
@property
|
||||
def main_image(self):
|
||||
qs = self.product_gallery.main_image()
|
||||
if qs.exists():
|
||||
return qs.first().image
|
||||
|
||||
@property
|
||||
def image_url(self):
|
||||
return self.main_image.image.url if self.main_image else None
|
||||
|
||||
@property
|
||||
def preview_image_url(self):
|
||||
if self.main_image:
|
||||
return self.main_image.get_image_url(thumbnail_key='product_preview')
|
||||
|
||||
|
||||
class OnlineProductManager(ProductManager):
|
||||
"""Extended manger for OnlineProduct model."""
|
||||
|
|
@ -353,15 +345,8 @@ class ProductStandard(models.Model):
|
|||
verbose_name = _('wine standard')
|
||||
|
||||
|
||||
class ProductGalleryQuerySet(models.QuerySet):
|
||||
"""QuerySet for model Product"""
|
||||
class ProductGallery(IntermediateGalleryModelMixin):
|
||||
|
||||
def main_image(self):
|
||||
"""Return objects with flag is_main is True"""
|
||||
return self.filter(is_main=True)
|
||||
|
||||
|
||||
class ProductGallery(models.Model):
|
||||
product = models.ForeignKey(Product, null=True,
|
||||
related_name='product_gallery',
|
||||
on_delete=models.CASCADE,
|
||||
|
|
@ -369,11 +354,7 @@ class ProductGallery(models.Model):
|
|||
image = models.ForeignKey('gallery.Image', null=True,
|
||||
related_name='product_gallery',
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('gallery'))
|
||||
is_main = models.BooleanField(default=False,
|
||||
verbose_name=_('Is the main image'))
|
||||
|
||||
objects = ProductGalleryQuerySet.as_manager()
|
||||
verbose_name=_('image'))
|
||||
|
||||
class Meta:
|
||||
"""ProductGallery meta class."""
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from gallery.models import Image
|
|||
from product import models
|
||||
from review.serializers import ReviewShortSerializer
|
||||
from utils import exceptions as utils_exceptions
|
||||
from utils.serializers import TranslatedField, FavoritesCreateSerializer
|
||||
from utils.serializers import TranslatedField, FavoritesCreateSerializer, ImageBaseSerializer
|
||||
from main.serializers import AwardSerializer
|
||||
from location.serializers import WineRegionBaseSerializer, WineSubRegionBaseSerializer
|
||||
from tag.serializers import TagBaseSerializer, TagCategoryShortSerializer
|
||||
|
|
@ -83,6 +83,15 @@ class ProductStandardBaseSerializer(serializers.ModelSerializer):
|
|||
)
|
||||
|
||||
|
||||
class ProductCropImageSerializer(serializers.Serializer):
|
||||
"""Serializer for product image."""
|
||||
|
||||
|
||||
class ProductImageSerializer(ImageBaseSerializer):
|
||||
"""Serializer for product image."""
|
||||
auto_crop_images = ProductCropImageSerializer(allow_null=True)
|
||||
|
||||
|
||||
class ProductBaseSerializer(serializers.ModelSerializer):
|
||||
"""Product base serializer."""
|
||||
name = serializers.CharField(source='display_name', read_only=True)
|
||||
|
|
@ -92,8 +101,7 @@ class ProductBaseSerializer(serializers.ModelSerializer):
|
|||
tags = ProductTagSerializer(source='related_tags', many=True, read_only=True)
|
||||
wine_region = WineRegionBaseSerializer(read_only=True)
|
||||
wine_colors = TagBaseSerializer(many=True, read_only=True)
|
||||
preview_image_url = serializers.URLField(source='preview_main_image_url',
|
||||
allow_null=True,
|
||||
preview_image_url = serializers.URLField(allow_null=True,
|
||||
read_only=True)
|
||||
in_favorites = serializers.BooleanField(allow_null=True)
|
||||
|
||||
|
|
@ -127,9 +135,10 @@ class ProductDetailSerializer(ProductBaseSerializer):
|
|||
wine_sub_region = WineSubRegionBaseSerializer(read_only=True)
|
||||
bottles_produced = TagBaseSerializer(many=True, read_only=True)
|
||||
sugar_contents = TagBaseSerializer(many=True, read_only=True)
|
||||
image_url = serializers.ImageField(source='main_image_url',
|
||||
allow_null=True,
|
||||
read_only=True)
|
||||
image_url = serializers.URLField(allow_null=True,
|
||||
read_only=True)
|
||||
|
||||
new_image = ImageBaseSerializer(source='crop_main_image', allow_null=True, read_only=True)
|
||||
|
||||
class Meta(ProductBaseSerializer.Meta):
|
||||
fields = ProductBaseSerializer.Meta.fields + [
|
||||
|
|
@ -142,6 +151,7 @@ class ProductDetailSerializer(ProductBaseSerializer):
|
|||
'bottles_produced',
|
||||
'sugar_contents',
|
||||
'image_url',
|
||||
'new_image',
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -175,78 +185,6 @@ class ProductFavoritesCreateSerializer(FavoritesCreateSerializer):
|
|||
return super().create(validated_data)
|
||||
|
||||
|
||||
# class CropImageSerializer(serializers.Serializer):
|
||||
# """Serializer for crop images for News object."""
|
||||
#
|
||||
# preview_url = serializers.SerializerMethodField()
|
||||
# promo_horizontal_web_url = serializers.SerializerMethodField()
|
||||
# promo_horizontal_mobile_url = serializers.SerializerMethodField()
|
||||
# tile_horizontal_web_url = serializers.SerializerMethodField()
|
||||
# tile_horizontal_mobile_url = serializers.SerializerMethodField()
|
||||
# tile_vertical_web_url = serializers.SerializerMethodField()
|
||||
# highlight_vertical_web_url = serializers.SerializerMethodField()
|
||||
# editor_web_url = serializers.SerializerMethodField()
|
||||
# editor_mobile_url = serializers.SerializerMethodField()
|
||||
#
|
||||
# def get_preview_url(self, obj):
|
||||
# """Get crop preview."""
|
||||
# return obj.instance.get_image_url('news_preview')
|
||||
#
|
||||
# def get_promo_horizontal_web_url(self, obj):
|
||||
# """Get crop promo_horizontal_web."""
|
||||
# return obj.instance.get_image_url('news_promo_horizontal_web')
|
||||
#
|
||||
# def get_promo_horizontal_mobile_url(self, obj):
|
||||
# """Get crop promo_horizontal_mobile."""
|
||||
# return obj.instance.get_image_url('news_promo_horizontal_mobile')
|
||||
#
|
||||
# def get_tile_horizontal_web_url(self, obj):
|
||||
# """Get crop tile_horizontal_web."""
|
||||
# return obj.instance.get_image_url('news_tile_horizontal_web')
|
||||
#
|
||||
# def get_tile_horizontal_mobile_url(self, obj):
|
||||
# """Get crop tile_horizontal_mobile."""
|
||||
# return obj.instance.get_image_url('news_tile_horizontal_mobile')
|
||||
#
|
||||
# def get_tile_vertical_web_url(self, obj):
|
||||
# """Get crop tile_vertical_web."""
|
||||
# return obj.instance.get_image_url('news_tile_vertical_web')
|
||||
#
|
||||
# def get_highlight_vertical_web_url(self, obj):
|
||||
# """Get crop highlight_vertical_web."""
|
||||
# return obj.instance.get_image_url('news_highlight_vertical_web')
|
||||
#
|
||||
# def get_editor_web_url(self, obj):
|
||||
# """Get crop editor_web."""
|
||||
# return obj.instance.get_image_url('news_editor_web')
|
||||
#
|
||||
# def get_editor_mobile_url(self, obj):
|
||||
# """Get crop editor_mobile."""
|
||||
# return obj.instance.get_image_url('news_editor_mobile')
|
||||
|
||||
|
||||
class ProductImageSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for returning crop images of product image."""
|
||||
|
||||
orientation_display = serializers.CharField(source='get_orientation_display',
|
||||
read_only=True)
|
||||
original_url = serializers.URLField(source='image.url')
|
||||
# auto_crop_images = CropImageSerializer(source='image', allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = Image
|
||||
fields = [
|
||||
'id',
|
||||
'title',
|
||||
'orientation_display',
|
||||
'original_url',
|
||||
# 'auto_crop_images',
|
||||
]
|
||||
extra_kwargs = {
|
||||
'orientation': {'write_only': True}
|
||||
}
|
||||
|
||||
|
||||
class ProductCommentCreateSerializer(CommentSerializer):
|
||||
"""Create comment serializer"""
|
||||
mark = serializers.IntegerField()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from rest_framework.response import Response
|
|||
|
||||
from product import serializers, models
|
||||
from product.views import ProductBaseView
|
||||
from utils.serializers import ImageBaseSerializer
|
||||
from utils.views import CreateDestroyGalleryViewMixin
|
||||
|
||||
|
||||
|
|
@ -65,9 +66,11 @@ class ProductBackOfficeGalleryCreateDestroyView(ProductBackOfficeMixinView,
|
|||
return gallery
|
||||
|
||||
|
||||
class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView, generics.ListAPIView):
|
||||
class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView,
|
||||
generics.ListAPIView):
|
||||
"""Resource for returning gallery for product for back-office users."""
|
||||
serializer_class = serializers.ProductImageSerializer
|
||||
serializer_class = ImageBaseSerializer
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def get_object(self):
|
||||
"""Override get_object method."""
|
||||
|
|
@ -81,10 +84,11 @@ class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView, generics.List
|
|||
|
||||
def get_queryset(self):
|
||||
"""Override get_queryset method."""
|
||||
return self.get_object().gallery.all()
|
||||
return self.get_object().crop_gallery
|
||||
|
||||
|
||||
class ProductDetailBackOfficeView(ProductBackOfficeMixinView, generics.RetrieveUpdateDestroyAPIView):
|
||||
class ProductDetailBackOfficeView(ProductBackOfficeMixinView,
|
||||
generics.RetrieveUpdateDestroyAPIView):
|
||||
"""Product back-office R/U/D view."""
|
||||
serializer_class = serializers.ProductBackOfficeDetailSerializer
|
||||
|
||||
|
|
|
|||
19
apps/review/migrations/0018_auto_20191117_1117.py
Normal file
19
apps/review/migrations/0018_auto_20191117_1117.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.7 on 2019-11-17 11:17
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('review', '0017_auto_20191115_0737'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='inquiriesgallery',
|
||||
name='image',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inquiries_gallery', to='gallery.Image', verbose_name='image'),
|
||||
),
|
||||
]
|
||||
|
|
@ -4,8 +4,9 @@ from django.core.validators import MinValueValidator, MaxValueValidator
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from utils.models import BaseAttributes, TranslatedFieldsMixin, ProjectBaseMixin
|
||||
from utils.models import TJSONField
|
||||
from utils.models import (BaseAttributes, TranslatedFieldsMixin,
|
||||
ProjectBaseMixin, GalleryModelMixin,
|
||||
TJSONField, IntermediateGalleryModelMixin)
|
||||
|
||||
|
||||
class ReviewQuerySet(models.QuerySet):
|
||||
|
|
@ -92,7 +93,7 @@ class Review(BaseAttributes, TranslatedFieldsMixin):
|
|||
verbose_name_plural = _('Reviews')
|
||||
|
||||
|
||||
class Inquiries(ProjectBaseMixin):
|
||||
class Inquiries(GalleryModelMixin, ProjectBaseMixin):
|
||||
NONE = 0
|
||||
DINER = 1
|
||||
LUNCH = 2
|
||||
|
|
@ -145,15 +146,7 @@ class GridItems(ProjectBaseMixin):
|
|||
return f'inquiry: {self.inquiry.id}, grid id: {self.id}'
|
||||
|
||||
|
||||
class InquiriesGalleryQuerySet(models.QuerySet):
|
||||
"""QuerySet for model Inquiries"""
|
||||
|
||||
def main_image(self):
|
||||
"""Return objects with flag is_main is True"""
|
||||
return self.filter(is_main=True)
|
||||
|
||||
|
||||
class InquiriesGallery(models.Model):
|
||||
class InquiriesGallery(IntermediateGalleryModelMixin):
|
||||
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
|
||||
inquiry = models.ForeignKey(
|
||||
Inquiries,
|
||||
|
|
@ -167,11 +160,8 @@ class InquiriesGallery(models.Model):
|
|||
null=True,
|
||||
related_name='inquiries_gallery',
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_('gallery'),
|
||||
verbose_name=_('image'),
|
||||
)
|
||||
is_main = models.BooleanField(default=False, verbose_name=_('Is the main image'))
|
||||
|
||||
objects = InquiriesGalleryQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('inquiry gallery')
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ class Command(BaseCommand):
|
|||
'update_country_flag',
|
||||
'comment',
|
||||
'inquiries', # №6 - перенос запросов оценок
|
||||
'wine_characteristics',
|
||||
'product',
|
||||
'product_note',
|
||||
'souvenir',
|
||||
'establishment_note',
|
||||
'assemblage',
|
||||
'wine_characteristics', # №5 - перенос характиристик вин
|
||||
'product', # №5 - перенос продуктов
|
||||
'product_note', # №6 - перенос заметок продуктов
|
||||
'souvenir', # №5 - перенос продуктов типа - сувениры
|
||||
'establishment_note', # №5 - перенос заметок заведений
|
||||
'assemblage', # №6 - перенос тегов для типа продуктов - вино
|
||||
'rating_count',
|
||||
'product_review',
|
||||
'newsletter_subscriber', # подписчики на рассылку - переносить после переноса пользователей №1
|
||||
|
|
|
|||
|
|
@ -345,4 +345,77 @@ class GMTokenGenerator(PasswordResetTokenGenerator):
|
|||
return self.get_fields(user, timestamp)
|
||||
|
||||
|
||||
class GalleryModelMixin(models.Model):
|
||||
"""Mixin for models that has gallery."""
|
||||
|
||||
@property
|
||||
def crop_gallery(self):
|
||||
if hasattr(self, 'gallery'):
|
||||
gallery = []
|
||||
images = self.gallery.all()
|
||||
crop_parameters = [p for p in settings.SORL_THUMBNAIL_ALIASES
|
||||
if p.startswith(self._meta.model_name.lower())]
|
||||
for image in images:
|
||||
d = {
|
||||
'id': image.id,
|
||||
'title': image.title,
|
||||
'original_url': image.image.url,
|
||||
'orientation_display': image.get_orientation_display(),
|
||||
'auto_crop_images': {},
|
||||
}
|
||||
for crop in crop_parameters:
|
||||
d['auto_crop_images'].update({crop: image.get_image_url(crop)})
|
||||
gallery.append(d)
|
||||
return gallery
|
||||
|
||||
@property
|
||||
def crop_main_image(self):
|
||||
if hasattr(self, 'main_image') and self.main_image:
|
||||
image = self.main_image
|
||||
image_property = {
|
||||
'id': image.id,
|
||||
'title': image.title,
|
||||
'original_url': image.image.url,
|
||||
'orientation_display': image.get_orientation_display(),
|
||||
'auto_crop_images': {},
|
||||
}
|
||||
crop_parameters = [p for p in settings.SORL_THUMBNAIL_ALIASES
|
||||
if p.startswith(self._meta.model_name.lower())]
|
||||
for crop in crop_parameters:
|
||||
image_property['auto_crop_images'].update(
|
||||
{crop: image.get_image_url(crop)}
|
||||
)
|
||||
return image_property
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
abstract = True
|
||||
|
||||
|
||||
class IntermediateGalleryModelQuerySet(models.QuerySet):
|
||||
"""Extended QuerySet."""
|
||||
|
||||
def main_image(self):
|
||||
"""Return objects with flag is_main is True"""
|
||||
return self.filter(is_main=True)
|
||||
|
||||
|
||||
class IntermediateGalleryModelMixin(models.Model):
|
||||
"""Mixin for intermediate gallery model."""
|
||||
|
||||
is_main = models.BooleanField(default=False,
|
||||
verbose_name=_('Is the main image'))
|
||||
|
||||
objects = IntermediateGalleryModelQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
"""Overridden str method."""
|
||||
if hasattr(self, 'image'):
|
||||
return self.image.title if self.image.title else self.id
|
||||
|
||||
|
||||
timezone.datetime.now().date().isoformat()
|
||||
|
|
@ -5,6 +5,7 @@ from rest_framework import serializers
|
|||
from utils import models
|
||||
from translation.models import Language
|
||||
from favorites.models import Favorites
|
||||
from gallery.models import Image
|
||||
|
||||
|
||||
class EmptySerializer(serializers.Serializer):
|
||||
|
|
@ -104,3 +105,13 @@ class RecursiveFieldSerializer(serializers.Serializer):
|
|||
def to_representation(self, value):
|
||||
serializer = self.parent.parent.__class__(value, context=self.context)
|
||||
return serializer.data
|
||||
|
||||
|
||||
class ImageBaseSerializer(serializers.Serializer):
|
||||
"""Serializer for returning crop images of model image."""
|
||||
|
||||
id = serializers.IntegerField()
|
||||
title = serializers.CharField()
|
||||
original_url = serializers.URLField()
|
||||
orientation_display = serializers.CharField()
|
||||
auto_crop_images = serializers.DictField(allow_null=True)
|
||||
|
|
|
|||
|
|
@ -380,6 +380,14 @@ SORL_THUMBNAIL_ALIASES = {
|
|||
'news_editor_mobile': {'geometry_string': '343x260', 'crop': 'center'}, # при загрузке через контент эдитор
|
||||
'avatar_comments_web': {'geometry_string': '116x116', 'crop': 'center'}, # через контент эдитор в мобильном браузерe
|
||||
'product_preview': {'geometry_string': '300x260', 'crop': 'center'},
|
||||
'establishment_preview': {'geometry_string': '300x260', 'crop': 'center'},
|
||||
'establishment_xsmall': {'geometry_string': '60x34', 'crop': 'center'},
|
||||
'establishment_small': {'geometry_string': '80x45', 'crop': 'center'},
|
||||
'establishment_medium': {'geometry_string': '280x158', 'crop': 'center'},
|
||||
'establishment_large': {'geometry_string': '440x248', 'crop': 'center'},
|
||||
'establishment_xlarge': {'geometry_string': '640x360', 'crop': 'center'},
|
||||
'establishment_detail': {'geometry_string': '2048x1152', 'crop': 'center'},
|
||||
'establishment_original': {'geometry_string': '1920x1080', 'crop': 'center'},
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user