From f7703f18b05cc357c47ef134c2284e6c7d93c558 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 22 Nov 2019 13:02:04 +0300 Subject: [PATCH] refactored upload image endpoint --- apps/gallery/serializers.py | 54 +++++++++++++++++++++++++++++++++++++ apps/utils/models.py | 12 +++++++++ project/settings/base.py | 7 +++++ 3 files changed, 73 insertions(+) diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index e817cbd8..1f96dca8 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -1,4 +1,8 @@ +from django.conf import settings +from django.core.validators import MinValueValidator, MaxValueValidator from rest_framework import serializers +from sorl.thumbnail.parsers import parse_crop +from sorl.thumbnail.parsers import ThumbnailParseError from . import models @@ -8,10 +12,21 @@ class ImageSerializer(serializers.ModelSerializer): # REQUEST file = serializers.ImageField(source='image', write_only=True) + width = serializers.IntegerField(write_only=True, required=False) + height = serializers.IntegerField(write_only=True, required=False) + margin = serializers.CharField(write_only=True, allow_null=True, + required=False, + default='center') + quality = serializers.IntegerField(write_only=True, allow_null=True, required=False, + default=settings.THUMBNAIL_QUALITY, + validators=[ + MinValueValidator(1), + MaxValueValidator(100)]) # RESPONSE url = serializers.ImageField(source='image', read_only=True) + cropped_image = serializers.DictField(read_only=True, allow_null=True) orientation_display = serializers.CharField(source='get_orientation_display', read_only=True) @@ -25,7 +40,46 @@ class ImageSerializer(serializers.ModelSerializer): 'orientation', 'orientation_display', 'title', + 'width', + 'height', + 'margin', + 'quality', + 'cropped_image', ] extra_kwargs = { 'orientation': {'write_only': True} } + + def validate(self, attrs): + """Overridden validate method.""" + image = attrs.get('image').image + crop_width = attrs.get('width') + crop_height = attrs.get('height') + margin = attrs.get('margin') + + if crop_height and crop_width and margin: + xy_image = (image.width, image.width) + xy_window = (crop_width, crop_height) + try: + parse_crop(margin, xy_image, xy_window) + except ThumbnailParseError: + raise serializers.ValidationError({'margin': 'Unrecognized crop option: %s' % margin}) + return attrs + + def create(self, validated_data): + """Overridden create method.""" + width = validated_data.pop('width', None) + height = validated_data.pop('height', None) + quality = validated_data.pop('quality') + margin = validated_data.pop('margin') + + instance = super().create(validated_data) + + if instance and width and height: + setattr(instance, + 'cropped_image', + instance.get_cropped_image( + geometry=f'{width}x{height}', + quality=quality, + margin=margin)) + return instance diff --git a/apps/utils/models.py b/apps/utils/models.py index 8c186f2f..59ec2282 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -227,6 +227,18 @@ class SORLImageMixin(models.Model): else: return None + def get_cropped_image(self, geometry: str, quality: int, margin: str) -> dict: + cropped_image = get_thumbnail(self.image, + geometry_string=geometry, + crop=margin, + quality=quality) + return { + 'geometry_string': geometry, + 'crop_url': cropped_image.url, + 'quality': quality, + 'margin': margin + } + image_tag.short_description = _('Image') image_tag.allow_tags = True diff --git a/project/settings/base.py b/project/settings/base.py index 2e29c92d..ef875c72 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -399,6 +399,13 @@ SORL_THUMBNAIL_ALIASES = { 'establishment_xlarge': {'geometry_string': '640x360', 'crop': 'center'}, 'establishment_detail': {'geometry_string': '2048x1152', 'crop': 'center'}, 'establishment_original': {'geometry_string': '1920x1080', 'crop': 'center'}, + 'city_xsmall': {'geometry_string': '70x70', 'crop': 'center'}, + 'city_small': {'geometry_string': '140x140', 'crop': 'center'}, + 'city_medium': {'geometry_string': '280x280', 'crop': 'center'}, + 'city_large': {'geometry_string': '280x280', 'crop': 'center'}, + 'city_xlarge': {'geometry_string': '560x560', 'crop': 'center'}, + 'city_detail': {'geometry_string': '1120x1120', 'crop': 'center'}, + 'city_original': {'geometry_string': '2048x1536', 'crop': 'center'}, }