Files
Pinry/core/serializers.py
2020-02-11 19:47:50 +08:00

263 lines
7.6 KiB
Python

from django.conf import settings
from django.db.models import Q
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
def filter_private_pin(request, query):
if request.user.is_authenticated:
query = query.exclude(~Q(submitter=request.user), private=True)
else:
query = query.exclude(private=True)
return query.select_related('image', 'submitter')
def filter_private_board(request, query):
if request.user.is_authenticated:
query = query.exclude(~Q(submitter=request.user), private=True)
else:
query = query.exclude(private=True)
return query
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,
"private",
"id",
"submitter",
"url",
"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",
"private",
"pins",
"pins_detail",
"published",
"submitter",
"pins_to_add",
"pins_to_remove",
)
read_only_fields = ('submitter', 'published')
extra_kwargs = {
'submitter': {"view_name": "users:user-detail"},
}
submitter = UserSerializer(read_only=True)
pins_detail = serializers.SerializerMethodField(
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"
)
def get_pins_detail(self, instance):
query = instance.pins.all()
request = self.context['request']
query = filter_private_pin(request, query)
return [PinSerializer(pin, context=self.context).data for pin in query]
@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", [])
board = Board.objects.filter(
submitter=instance.submitter,
name=validated_data.get('name', None)
).first()
if board and board.id != instance.id:
raise ValidationError(
detail={'name': "Board with this name already exists"}
)
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_add', 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)
class TagAutoCompleteSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('name', )