refactored, add endpoint - /api/back/gallery/<pk>/crop/

This commit is contained in:
Anatoly 2019-11-22 14:46:31 +03:00
parent 59f44d84f1
commit 844c8525e9
3 changed files with 64 additions and 24 deletions

View File

@ -3,6 +3,7 @@ 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 django.utils.translation import gettext_lazy as _
from . import models
@ -12,21 +13,10 @@ 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)
@ -40,30 +30,55 @@ class ImageSerializer(serializers.ModelSerializer):
'orientation',
'orientation_display',
'title',
]
extra_kwargs = {
'orientation': {'write_only': True}
}
class CropImageSerializer(ImageSerializer):
"""Serializers for image crops."""
width = serializers.IntegerField(write_only=True)
height = serializers.IntegerField(write_only=True)
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)])
cropped_image = serializers.DictField(read_only=True, allow_null=True)
class Meta(ImageSerializer.Meta):
"""Meta class."""
fields = [
'id',
'url',
'orientation_display',
'width',
'height',
'margin',
'quality',
'cropped_image',
]
extra_kwargs = {
'orientation': {'write_only': True}
}
def validate(self, attrs):
"""Overridden validate method."""
image = attrs.get('image').image
file = self._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_image = (file.width, file.width)
xy_window = (crop_width, crop_height)
try:
parse_crop(margin, xy_image, xy_window)
attrs['image'] = file
except ThumbnailParseError:
raise serializers.ValidationError({'margin': 'Unrecognized crop option: %s' % margin})
raise serializers.ValidationError({'margin': _('Unrecognized crop option: %s') % margin})
return attrs
def create(self, validated_data):
@ -73,13 +88,32 @@ class ImageSerializer(serializers.ModelSerializer):
quality = validated_data.pop('quality')
margin = validated_data.pop('margin')
instance = super().create(validated_data)
image = self._image
if instance and width and height:
setattr(instance,
if image and width and height:
setattr(image,
'cropped_image',
instance.get_cropped_image(
image.get_cropped_image(
geometry=f'{width}x{height}',
quality=quality,
margin=margin))
return instance
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.')})

View File

@ -6,6 +6,7 @@ from . import views
app_name = 'gallery'
urlpatterns = [
path('', views.ImageListCreateView.as_view(), name='list-create-image'),
path('<int:pk>/', views.ImageRetrieveDestroyView.as_view(), name='retrieve-destroy-image'),
path('', views.ImageListCreateView.as_view(), name='list-create'),
path('<int:pk>/', views.ImageRetrieveDestroyView.as_view(), name='retrieve-destroy'),
path('<int:pk>/crop/', views.CropImageCreateView.as_view(), name='create-crop'),
]

View File

@ -28,3 +28,8 @@ class ImageRetrieveDestroyView(ImageBaseView, generics.RetrieveDestroyAPIView):
else:
on_commit(lambda: tasks.delete_image(image_id=instance.id))
return Response(status=status.HTTP_204_NO_CONTENT)
class CropImageCreateView(ImageBaseView, generics.CreateAPIView):
"""Create crop image."""
serializer_class = serializers.CropImageSerializer