217 lines
8.0 KiB
Python
217 lines
8.0 KiB
Python
from django.core.validators import MinValueValidator, MaxValueValidator
|
|
from django.core.files.base import ContentFile
|
|
from rest_framework import serializers
|
|
from sorl.thumbnail import get_thumbnail
|
|
from sorl.thumbnail.parsers import parse_crop, ThumbnailParseError
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.conf import settings
|
|
from django.shortcuts import get_object_or_404
|
|
from establishment.models import Establishment, EstablishmentGallery
|
|
from account.serializers.common import UserBaseSerializer
|
|
from . import models
|
|
|
|
|
|
class ImageSerializer(serializers.ModelSerializer):
|
|
"""Serializer for model Image."""
|
|
# REQUEST
|
|
file = serializers.ImageField(source='image',
|
|
write_only=True)
|
|
|
|
# RESPONSE
|
|
url = serializers.ImageField(source='image',
|
|
read_only=True)
|
|
orientation_display = serializers.CharField(source='get_orientation_display',
|
|
read_only=True)
|
|
|
|
class Meta:
|
|
"""Meta class"""
|
|
model = models.Image
|
|
fields = [
|
|
'id',
|
|
'file',
|
|
'url',
|
|
'orientation',
|
|
'orientation_display',
|
|
'title',
|
|
]
|
|
extra_kwargs = {
|
|
'orientation': {'write_only': True}
|
|
}
|
|
|
|
def validate(self, attrs):
|
|
"""Overridden validate method."""
|
|
image = attrs.get('image')
|
|
|
|
if image and image.size >= settings.FILE_UPLOAD_MAX_MEMORY_SIZE:
|
|
raise serializers.ValidationError({'detail': _('File size too large: %s bytes') % image.size})
|
|
|
|
return attrs
|
|
|
|
|
|
class EstablishmentGallerySerializer(serializers.ModelSerializer):
|
|
"""Serializer for creating and retrieving establishment media"""
|
|
type = serializers.ChoiceField(read_only=True, choices=models.Image.MEDIA_TYPES)
|
|
created_by = UserBaseSerializer(read_only=True, allow_null=True)
|
|
image_size_in_KB = serializers.DecimalField(read_only=True, decimal_places=2, max_digits=20)
|
|
is_main = serializers.BooleanField()
|
|
cropped_image = serializers.ImageField(source='image_by_cropbox', allow_null=True, read_only=True)
|
|
|
|
class Meta:
|
|
model = models.Image
|
|
fields = (
|
|
'id',
|
|
'image',
|
|
'type',
|
|
'link',
|
|
'order',
|
|
'preview',
|
|
'is_public',
|
|
'title',
|
|
'is_main',
|
|
'created_by',
|
|
'created',
|
|
'image_size_in_KB',
|
|
'cropbox',
|
|
'cropped_image',
|
|
)
|
|
extra_kwargs = {
|
|
'created': {'read_only': True},
|
|
'created_by': {'read_only': True},
|
|
}
|
|
|
|
def validate(self, attrs):
|
|
"""Overridden validate method."""
|
|
image = attrs.get('image')
|
|
|
|
if image and image.size >= settings.FILE_UPLOAD_MAX_MEMORY_SIZE:
|
|
raise serializers.ValidationError({'detail': _('File size too large: %s bytes') % image.size})
|
|
if attrs.get('cropbox'):
|
|
if len(attrs['cropbox'].split(',')) != 4:
|
|
raise serializers.ValidationError({'detail': _('Cropbox contains 4 integer values separated by comma.')})
|
|
return attrs
|
|
|
|
def create(self, validated_data):
|
|
is_main = validated_data.pop('is_main')
|
|
establishment = get_object_or_404(klass=Establishment, pk=self.context['view'].kwargs['establishment_id'])
|
|
instance = super().create(validated_data)
|
|
instance.created_by = self.context['request'].user
|
|
instance.establishment_set.add(establishment)
|
|
instance.save()
|
|
if is_main:
|
|
EstablishmentGallery.objects.filter(
|
|
establishment=establishment
|
|
).update(is_main=False) # reset all before setting True on some instance
|
|
EstablishmentGallery.objects.filter(
|
|
image=instance
|
|
).update(is_main=is_main)
|
|
return instance
|
|
|
|
def update(self, instance: models.Image, validated_data):
|
|
if instance.is_public != validated_data.get('is_public'):
|
|
instance.set_pubic(validated_data.get('is_public', True))
|
|
if 'is_main' in validated_data:
|
|
is_main = validated_data.pop('is_main')
|
|
if is_main:
|
|
establishment = instance.establishment_gallery.all()[0].establishment
|
|
EstablishmentGallery.objects.filter(
|
|
establishment=establishment
|
|
).update(is_main=False) # reset all before setting True on some instance
|
|
EstablishmentGallery.objects.filter(
|
|
image=instance
|
|
).update(is_main=is_main)
|
|
return super().update(instance, validated_data)
|
|
|
|
|
|
class CropImageSerializer(ImageSerializer):
|
|
"""Serializers for image crops."""
|
|
|
|
width = serializers.IntegerField(write_only=True, required=False)
|
|
height = serializers.IntegerField(write_only=True, required=False)
|
|
crop = serializers.CharField(write_only=True,
|
|
required=False,
|
|
default='center')
|
|
quality = serializers.IntegerField(write_only=True, required=False,
|
|
default=settings.THUMBNAIL_QUALITY,
|
|
validators=[
|
|
MinValueValidator(1),
|
|
MaxValueValidator(100)])
|
|
cropped_image = serializers.DictField(read_only=True, allow_null=True)
|
|
certain_aspect = serializers.CharField(allow_blank=True, allow_null=True, required=False)
|
|
|
|
class Meta(ImageSerializer.Meta):
|
|
"""Meta class."""
|
|
fields = [
|
|
'id',
|
|
'url',
|
|
'orientation_display',
|
|
'width',
|
|
'height',
|
|
'crop',
|
|
'quality',
|
|
'cropped_image',
|
|
'certain_aspect',
|
|
]
|
|
|
|
def validate(self, attrs):
|
|
"""Overridden validate method."""
|
|
file = self._image.image
|
|
crop_width = attrs.get('width')
|
|
crop_height = attrs.get('height')
|
|
crop = attrs.get('crop')
|
|
|
|
if (crop_height and crop_width) and (crop and crop != 'smart'):
|
|
xy_image = (file.width, file.width)
|
|
xy_window = (crop_width, crop_height)
|
|
try:
|
|
parse_crop(crop, xy_image, xy_window)
|
|
attrs['image'] = file
|
|
except ThumbnailParseError:
|
|
raise serializers.ValidationError({'margin': _('Unrecognized crop option: %s') % crop})
|
|
return attrs
|
|
|
|
def create(self, validated_data):
|
|
"""Overridden create method."""
|
|
width = validated_data.pop('width', None)
|
|
height = validated_data.pop('height', None)
|
|
crop = validated_data.pop('crop')
|
|
x1, y1 = int(crop.split(' ')[0][:-2]), int(crop.split(' ')[1][:-2])
|
|
x2, y2 = x1 + width, y1 + height
|
|
crop_params = {
|
|
'geometry': f'{round(x2 - x1)}x{round(y2 - y1)}' if 'certain_aspect' not in validated_data else
|
|
validated_data['certain_aspect'],
|
|
'quality': 100,
|
|
'cropbox': f'{x1},{y1},{x2},{y2}'
|
|
}
|
|
cropped_image = self._image.get_cropped_image(**crop_params)
|
|
image = self._image
|
|
image.pk = None
|
|
crop_params['geometry_string'] = crop_params.pop('geometry')
|
|
resized = get_thumbnail(self._image.image, **crop_params)
|
|
image.image.save(resized.name, ContentFile(resized.read()), True)
|
|
image.save()
|
|
|
|
if image and width and height:
|
|
setattr(image,
|
|
'cropped_image',
|
|
cropped_image)
|
|
return image
|
|
|
|
@property
|
|
def view(self):
|
|
return self.context.get('view')
|
|
|
|
@property
|
|
def lookup_field(self):
|
|
lookup_field = 'pk'
|
|
|
|
if lookup_field in self.view.kwargs:
|
|
return self.view.kwargs.get(lookup_field)
|
|
|
|
@property
|
|
def _image(self):
|
|
"""Return image from url_kwargs."""
|
|
qs = models.Image.objects.filter(id=self.lookup_field)
|
|
if qs.exists():
|
|
return qs.first()
|
|
raise serializers.ValidationError({'detail': _('Image not found.')})
|