refactored, add endpoint - /api/back/gallery/<pk>/crop/
This commit is contained in:
parent
59f44d84f1
commit
844c8525e9
|
|
@ -3,6 +3,7 @@ from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from sorl.thumbnail.parsers import parse_crop
|
from sorl.thumbnail.parsers import parse_crop
|
||||||
from sorl.thumbnail.parsers import ThumbnailParseError
|
from sorl.thumbnail.parsers import ThumbnailParseError
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
|
@ -12,21 +13,10 @@ class ImageSerializer(serializers.ModelSerializer):
|
||||||
# REQUEST
|
# REQUEST
|
||||||
file = serializers.ImageField(source='image',
|
file = serializers.ImageField(source='image',
|
||||||
write_only=True)
|
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
|
# RESPONSE
|
||||||
url = serializers.ImageField(source='image',
|
url = serializers.ImageField(source='image',
|
||||||
read_only=True)
|
read_only=True)
|
||||||
cropped_image = serializers.DictField(read_only=True, allow_null=True)
|
|
||||||
orientation_display = serializers.CharField(source='get_orientation_display',
|
orientation_display = serializers.CharField(source='get_orientation_display',
|
||||||
read_only=True)
|
read_only=True)
|
||||||
|
|
||||||
|
|
@ -40,30 +30,55 @@ class ImageSerializer(serializers.ModelSerializer):
|
||||||
'orientation',
|
'orientation',
|
||||||
'orientation_display',
|
'orientation_display',
|
||||||
'title',
|
'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',
|
'width',
|
||||||
'height',
|
'height',
|
||||||
'margin',
|
'margin',
|
||||||
'quality',
|
'quality',
|
||||||
'cropped_image',
|
'cropped_image',
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
|
||||||
'orientation': {'write_only': True}
|
|
||||||
}
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
"""Overridden validate method."""
|
"""Overridden validate method."""
|
||||||
image = attrs.get('image').image
|
file = self._image.image
|
||||||
crop_width = attrs.get('width')
|
crop_width = attrs.get('width')
|
||||||
crop_height = attrs.get('height')
|
crop_height = attrs.get('height')
|
||||||
margin = attrs.get('margin')
|
margin = attrs.get('margin')
|
||||||
|
|
||||||
if crop_height and crop_width and 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)
|
xy_window = (crop_width, crop_height)
|
||||||
try:
|
try:
|
||||||
parse_crop(margin, xy_image, xy_window)
|
parse_crop(margin, xy_image, xy_window)
|
||||||
|
attrs['image'] = file
|
||||||
except ThumbnailParseError:
|
except ThumbnailParseError:
|
||||||
raise serializers.ValidationError({'margin': 'Unrecognized crop option: %s' % margin})
|
raise serializers.ValidationError({'margin': _('Unrecognized crop option: %s') % margin})
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
|
@ -73,13 +88,32 @@ class ImageSerializer(serializers.ModelSerializer):
|
||||||
quality = validated_data.pop('quality')
|
quality = validated_data.pop('quality')
|
||||||
margin = validated_data.pop('margin')
|
margin = validated_data.pop('margin')
|
||||||
|
|
||||||
instance = super().create(validated_data)
|
image = self._image
|
||||||
|
|
||||||
if instance and width and height:
|
if image and width and height:
|
||||||
setattr(instance,
|
setattr(image,
|
||||||
'cropped_image',
|
'cropped_image',
|
||||||
instance.get_cropped_image(
|
image.get_cropped_image(
|
||||||
geometry=f'{width}x{height}',
|
geometry=f'{width}x{height}',
|
||||||
quality=quality,
|
quality=quality,
|
||||||
margin=margin))
|
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.')})
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ from . import views
|
||||||
app_name = 'gallery'
|
app_name = 'gallery'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.ImageListCreateView.as_view(), name='list-create-image'),
|
path('', views.ImageListCreateView.as_view(), name='list-create'),
|
||||||
path('<int:pk>/', views.ImageRetrieveDestroyView.as_view(), name='retrieve-destroy-image'),
|
path('<int:pk>/', views.ImageRetrieveDestroyView.as_view(), name='retrieve-destroy'),
|
||||||
|
path('<int:pk>/crop/', views.CropImageCreateView.as_view(), name='create-crop'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -28,3 +28,8 @@ class ImageRetrieveDestroyView(ImageBaseView, generics.RetrieveDestroyAPIView):
|
||||||
else:
|
else:
|
||||||
on_commit(lambda: tasks.delete_image(image_id=instance.id))
|
on_commit(lambda: tasks.delete_image(image_id=instance.id))
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class CropImageCreateView(ImageBaseView, generics.CreateAPIView):
|
||||||
|
"""Create crop image."""
|
||||||
|
serializer_class = serializers.CropImageSerializer
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user