選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

263 行
7.6 KiB

  1. from django.conf import settings
  2. from django.db.models import Q
  3. from rest_framework import serializers
  4. from rest_framework.exceptions import ValidationError
  5. from taggit.models import Tag
  6. from core.models import Image, Board
  7. from core.models import Pin
  8. from django_images.models import Thumbnail
  9. from users.serializers import UserSerializer
  10. def filter_private_pin(request, query):
  11. if request.user.is_authenticated:
  12. query = query.exclude(~Q(submitter=request.user), private=True)
  13. else:
  14. query = query.exclude(private=True)
  15. return query.select_related('image', 'submitter')
  16. def filter_private_board(request, query):
  17. if request.user.is_authenticated:
  18. query = query.exclude(~Q(submitter=request.user), private=True)
  19. else:
  20. query = query.exclude(private=True)
  21. return query
  22. class ThumbnailSerializer(serializers.HyperlinkedModelSerializer):
  23. class Meta:
  24. model = Thumbnail
  25. fields = (
  26. "image",
  27. "width",
  28. "height",
  29. )
  30. class ImageSerializer(serializers.ModelSerializer):
  31. class Meta:
  32. model = Image
  33. fields = (
  34. "id",
  35. "image",
  36. "width",
  37. "height",
  38. "standard",
  39. "thumbnail",
  40. "square",
  41. )
  42. extra_kwargs = {
  43. "width": {"read_only": True},
  44. "height": {"read_only": True},
  45. }
  46. standard = ThumbnailSerializer(read_only=True)
  47. thumbnail = ThumbnailSerializer(read_only=True)
  48. square = ThumbnailSerializer(read_only=True)
  49. def create(self, validated_data):
  50. image = super(ImageSerializer, self).create(validated_data)
  51. Thumbnail.objects.get_or_create_at_sizes(image, settings.IMAGE_SIZES.keys())
  52. return image
  53. class TagSerializer(serializers.SlugRelatedField):
  54. class Meta:
  55. model = Tag
  56. fields = ("name",)
  57. queryset = Tag.objects.all()
  58. def __init__(self, **kwargs):
  59. super(TagSerializer, self).__init__(
  60. slug_field="name",
  61. **kwargs
  62. )
  63. def to_internal_value(self, data):
  64. obj, _ = self.get_queryset().get_or_create(
  65. defaults={self.slug_field: data, "slug": data},
  66. **{self.slug_field: data}
  67. )
  68. return obj
  69. class PinSerializer(serializers.HyperlinkedModelSerializer):
  70. class Meta:
  71. model = Pin
  72. fields = (
  73. settings.DRF_URL_FIELD_NAME,
  74. "private",
  75. "id",
  76. "submitter",
  77. "url",
  78. "description",
  79. "referer",
  80. "image",
  81. "image_by_id",
  82. "tags",
  83. )
  84. submitter = UserSerializer(read_only=True)
  85. tags = TagSerializer(
  86. many=True,
  87. source="tag_list",
  88. required=False,
  89. )
  90. image = ImageSerializer(required=False, read_only=True)
  91. image_by_id = serializers.PrimaryKeyRelatedField(
  92. queryset=Image.objects.all(),
  93. write_only=True,
  94. required=False,
  95. )
  96. def create(self, validated_data):
  97. if 'url' not in validated_data and\
  98. 'image_by_id' not in validated_data:
  99. raise ValidationError(
  100. detail={
  101. "url-or-image": "Either url or image_by_id is required."
  102. },
  103. )
  104. submitter = self.context['request'].user
  105. if 'url' in validated_data and validated_data['url']:
  106. url = validated_data['url']
  107. image = Image.objects.create_for_url(
  108. url,
  109. validated_data.get('referer', url),
  110. )
  111. else:
  112. image = validated_data.pop("image_by_id")
  113. tags = validated_data.pop('tag_list', [])
  114. pin = Pin.objects.create(submitter=submitter, image=image, **validated_data)
  115. if tags:
  116. pin.tags.set(*tags)
  117. return pin
  118. def update(self, instance, validated_data):
  119. tags = validated_data.pop('tag_list', None)
  120. if tags:
  121. instance.tags.set(*tags)
  122. # change for image-id or image is not allowed
  123. validated_data.pop('image_by_id', None)
  124. return super(PinSerializer, self).update(instance, validated_data)
  125. class PinIdListField(serializers.ListField):
  126. child = serializers.IntegerField(
  127. min_value=1
  128. )
  129. class BoardAutoCompleteSerializer(serializers.HyperlinkedModelSerializer):
  130. class Meta:
  131. model = Board
  132. fields = (
  133. settings.DRF_URL_FIELD_NAME,
  134. 'id',
  135. 'name',
  136. )
  137. class BoardSerializer(serializers.HyperlinkedModelSerializer):
  138. class Meta:
  139. model = Board
  140. fields = (
  141. settings.DRF_URL_FIELD_NAME,
  142. "id",
  143. "name",
  144. "private",
  145. "pins",
  146. "pins_detail",
  147. "published",
  148. "submitter",
  149. "pins_to_add",
  150. "pins_to_remove",
  151. )
  152. read_only_fields = ('submitter', 'published')
  153. extra_kwargs = {
  154. 'submitter': {"view_name": "users:user-detail"},
  155. }
  156. submitter = UserSerializer(read_only=True)
  157. pins_detail = serializers.SerializerMethodField(
  158. read_only=True,
  159. )
  160. pins = serializers.HyperlinkedRelatedField(
  161. write_only=True,
  162. queryset=Pin.objects.all(),
  163. view_name="pin-detail",
  164. many=True,
  165. required=False,
  166. )
  167. pins_to_add = PinIdListField(
  168. max_length=10,
  169. write_only=True,
  170. required=False,
  171. allow_empty=False,
  172. help_text="only patch method works for this field",
  173. )
  174. pins_to_remove = PinIdListField(
  175. max_length=10,
  176. write_only=True,
  177. required=False,
  178. allow_empty=False,
  179. help_text="only patch method works for this field"
  180. )
  181. def get_pins_detail(self, instance):
  182. query = instance.pins.all()
  183. request = self.context['request']
  184. query = filter_private_pin(request, query)
  185. return [PinSerializer(pin, context=self.context).data for pin in query]
  186. @staticmethod
  187. def _get_list(pins_id):
  188. return tuple(Pin.objects.filter(id__in=pins_id))
  189. def update(self, instance: Board, validated_data):
  190. pins_to_add = validated_data.pop("pins_to_add", [])
  191. pins_to_remove = validated_data.pop("pins_to_remove", [])
  192. board = Board.objects.filter(
  193. submitter=instance.submitter,
  194. name=validated_data.get('name', None)
  195. ).first()
  196. if board.id != instance.id:
  197. raise ValidationError(
  198. detail={'name': "Board with this name already exists"}
  199. )
  200. instance = super(BoardSerializer, self).update(instance, validated_data)
  201. changed = False
  202. if pins_to_add:
  203. changed = True
  204. for pin in self._get_list(pins_to_add):
  205. instance.pins.add(pin)
  206. if pins_to_remove:
  207. changed = True
  208. for pin in self._get_list(pins_to_remove):
  209. instance.pins.remove(pin)
  210. if changed:
  211. instance.save()
  212. return instance
  213. def create(self, validated_data):
  214. validated_data.pop('pins_to_remove', None)
  215. validated_data.pop('pins_to_add', None)
  216. user = self.context['request'].user
  217. if Board.objects.filter(name=validated_data['name'], submitter=user).exists():
  218. raise ValidationError(
  219. detail={"name": "board with this name already exists."}
  220. )
  221. validated_data['submitter'] = user
  222. return super(BoardSerializer, self).create(validated_data)
  223. class TagAutoCompleteSerializer(serializers.ModelSerializer):
  224. class Meta:
  225. model = Tag
  226. fields = ('name', )