104 lines
4.1 KiB
Python
104 lines
4.1 KiB
Python
from django.db import transaction
|
|||
|
|
from rest_framework import serializers
|
||
|
|
|
||
|
|
from apps.accounts.serializers import UserSerializer
|
||
|
|
from apps.library.models import LibraryItem
|
||
|
|
from apps.library.serializers import LibraryItemSerializer
|
||
|
|
|
||
|
|
from .models import Comment, Moment, MomentItem, Reaction
|
||
|
|
|
||
|
|
|
||
|
|
class MomentItemSerializer(serializers.ModelSerializer):
|
||
|
|
library_item = LibraryItemSerializer(read_only=True)
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
model = MomentItem
|
||
|
|
fields = ["id", "library_item", "order"]
|
||
|
|
|
||
|
|
|
||
|
|
class CommentSerializer(serializers.ModelSerializer):
|
||
|
|
author = UserSerializer(read_only=True)
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
model = Comment
|
||
|
|
fields = ["id", "moment", "author", "parent", "body", "created_at"]
|
||
|
|
read_only_fields = ["id", "moment", "author", "created_at"]
|
||
|
|
|
||
|
|
def validate_parent(self, value):
|
||
|
|
if value is None:
|
||
|
|
return value
|
||
|
|
if value.parent_id is not None:
|
||
|
|
raise serializers.ValidationError("Cannot reply to a reply; reply to the original comment instead.")
|
||
|
|
moment = self.context.get("moment")
|
||
|
|
if moment is not None and value.moment_id != moment.id:
|
||
|
|
raise serializers.ValidationError("Reply must target a comment on the same Moment.")
|
||
|
|
return value
|
||
|
|
|
||
|
|
|
||
|
|
class ReactionSerializer(serializers.ModelSerializer):
|
||
|
|
user = UserSerializer(read_only=True)
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
model = Reaction
|
||
|
|
fields = ["id", "moment", "user", "reaction_type", "created_at"]
|
||
|
|
read_only_fields = ["id", "moment", "user", "created_at"]
|
||
|
|
|
||
|
|
|
||
|
|
class MomentSerializer(serializers.ModelSerializer):
|
||
|
|
owner = UserSerializer(read_only=True)
|
||
|
|
items = MomentItemSerializer(many=True, read_only=True)
|
||
|
|
library_item_ids = serializers.PrimaryKeyRelatedField(
|
||
|
|
queryset=LibraryItem.objects.all(), many=True, write_only=True, source="library_items", required=False
|
||
|
|
)
|
||
|
|
comment_count = serializers.IntegerField(source="comments.count", read_only=True)
|
||
|
|
reaction_count = serializers.IntegerField(source="reactions.count", read_only=True)
|
||
|
|
viewer_reaction_id = serializers.SerializerMethodField()
|
||
|
|
|
||
|
|
class Meta:
|
||
|
|
model = Moment
|
||
|
|
fields = [
|
||
|
|
"id", "owner", "caption", "location", "audience", "items",
|
||
|
|
"library_item_ids", "comment_count", "reaction_count", "viewer_reaction_id",
|
||
|
|
"created_at", "updated_at",
|
||
|
|
]
|
||
|
|
read_only_fields = [
|
||
|
|
"id", "owner", "items", "comment_count", "reaction_count", "viewer_reaction_id",
|
||
|
|
"created_at", "updated_at",
|
||
|
|
]
|
||
|
|
|
||
|
|
def get_viewer_reaction_id(self, obj):
|
||
|
|
request = self.context.get("request")
|
||
|
|
if not request or not request.user.is_authenticated:
|
||
|
|
return None
|
||
|
|
# obj.reactions is prefetched by the view, so this doesn't re-query per moment.
|
||
|
|
reaction = next((r for r in obj.reactions.all() if r.user_id == request.user.id), None)
|
||
|
|
return reaction.id if reaction else None
|
||
|
|
|
||
|
|
def validate_library_item_ids(self, library_items):
|
||
|
|
request = self.context["request"]
|
||
|
|
seen_ids = set()
|
||
|
|
for item in library_items:
|
||
|
|
if item.owner_id != request.user.id:
|
||
|
|
raise serializers.ValidationError("Can only share items from your own library.")
|
||
|
|
if item.id in seen_ids:
|
||
|
|
raise serializers.ValidationError("The same photo was selected more than once.")
|
||
|
|
seen_ids.add(item.id)
|
||
|
|
return library_items
|
||
|
|
|
||
|
|
def validate(self, attrs):
|
||
|
|
library_items = attrs.get("library_items", [])
|
||
|
|
caption = attrs.get("caption", "")
|
||
|
|
if not library_items and not caption.strip():
|
||
|
|
raise serializers.ValidationError("A Moment needs a caption or at least one photo.")
|
||
|
|
return attrs
|
||
|
|
|
||
|
|
def create(self, validated_data):
|
||
|
|
library_items = validated_data.pop("library_items", [])
|
||
|
|
validated_data["owner"] = self.context["request"].user
|
||
|
|
with transaction.atomic():
|
||
|
|
moment = Moment.objects.create(**validated_data)
|
||
|
|
MomentItem.objects.bulk_create(
|
||
|
|
[MomentItem(moment=moment, library_item=item, order=i) for i, item in enumerate(library_items)]
|
||
|
|
)
|
||
|
|
return moment
|