from django.conf import settings from rest_framework import serializers from rest_framework.exceptions import ValidationError from taggit.models import Tag from core.models import Image, Board from core.models import Pin from django_images.models import Thumbnail from users.serializers import UserSerializer class ThumbnailSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Thumbnail fields = ( "image", "width", "height", ) class ImageSerializer(serializers.ModelSerializer): class Meta: model = Image fields = ( "id", "image", "width", "height", "standard", "thumbnail", "square", ) extra_kwargs = { "width": {"read_only": True}, "height": {"read_only": True}, } standard = ThumbnailSerializer(read_only=True) thumbnail = ThumbnailSerializer(read_only=True) square = ThumbnailSerializer(read_only=True) def create(self, validated_data): image = super(ImageSerializer, self).create(validated_data) Thumbnail.objects.get_or_create_at_sizes(image, settings.IMAGE_SIZES.keys()) return image class TagSerializer(serializers.SlugRelatedField): class Meta: model = Tag fields = ("name",) queryset = Tag.objects.all() def __init__(self, **kwargs): super(TagSerializer, self).__init__( slug_field="name", **kwargs ) def to_internal_value(self, data): obj, _ = self.get_queryset().get_or_create( defaults={self.slug_field: data, "slug": data}, **{self.slug_field: data} ) return obj class PinSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Pin fields = ( settings.DRF_URL_FIELD_NAME, "id", "submitter", "url", "origin", "description", "referer", "image", "image_by_id", "tags", ) submitter = UserSerializer(read_only=True) tags = TagSerializer( many=True, source="tag_list", required=False, ) image = ImageSerializer(required=False, read_only=True) image_by_id = serializers.PrimaryKeyRelatedField( queryset=Image.objects.all(), write_only=True, required=False, ) def create(self, validated_data): if 'url' not in validated_data and\ 'image_by_id' not in validated_data: raise ValidationError( detail={ "url-or-image": "Either url or image_by_id is required." }, ) submitter = self.context['request'].user if 'url' in validated_data and validated_data['url']: url = validated_data['url'] image = Image.objects.create_for_url( url, validated_data.get('referer', url), ) else: image = validated_data.pop("image_by_id") tags = validated_data.pop('tag_list', []) pin = Pin.objects.create(submitter=submitter, image=image, **validated_data) if tags: pin.tags.set(*tags) return pin def update(self, instance, validated_data): tags = validated_data.pop('tag_list', None) if tags: instance.tags.set(*tags) # change for image-id or image is not allowed validated_data.pop('image_by_id', None) return super(PinSerializer, self).update(instance, validated_data) class PinIdListField(serializers.ListField): child = serializers.IntegerField( min_value=1 ) class BoardAutoCompleteSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Board fields = ( settings.DRF_URL_FIELD_NAME, 'id', 'name', ) class BoardSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Board fields = ( settings.DRF_URL_FIELD_NAME, "id", "name", "pins", "pins_detail", "published", "submitter", "pins_to_add", "pins_to_remove", ) read_only_fields = ('submitter', 'published') extra_kwargs = { 'submitter': {"view_name": "users:user-detail"}, } pins_detail = PinSerializer(source="pins", many=True, read_only=True) pins = serializers.HyperlinkedRelatedField( write_only=True, queryset=Pin.objects.all(), view_name="pin-detail", many=True, required=False, ) pins_to_add = PinIdListField( max_length=10, write_only=True, required=False, allow_empty=False, help_text="only patch method works for this field", ) pins_to_remove = PinIdListField( max_length=10, write_only=True, required=False, allow_empty=False, help_text="only patch method works for this field" ) @staticmethod def _get_list(pins_id): return tuple(Pin.objects.filter(id__in=pins_id)) def update(self, instance: Board, validated_data): pins_to_add = validated_data.pop("pins_to_add", []) pins_to_remove = validated_data.pop("pins_to_remove", []) instance = super(BoardSerializer, self).update(instance, validated_data) changed = False if pins_to_add: changed = True for pin in self._get_list(pins_to_add): instance.pins.add(pin) if pins_to_remove: changed = True for pin in self._get_list(pins_to_remove): instance.pins.remove(pin) if changed: instance.save() return instance def create(self, validated_data): validated_data.pop('pins_to_remove', None) validated_data.pop('pins_to_remove', None) user = self.context['request'].user if Board.objects.filter(name=validated_data['name'], submitter=user).exists(): raise ValidationError( detail={"name": "board with this name already exists."} ) validated_data['submitter'] = user return super(BoardSerializer, self).create(validated_data)