Django/DRF + PostgreSQL/Redis/Celery backend with accounts, library, moments, and notifications apps; React/TypeScript/Vite frontend with Instagram-style sidebar nav, feed, profiles, nested comment replies, notifications, and photo/video upload with ffmpeg-generated posters. Co-Authored-By: Claude Sonnet 5 <[email protected]>
92 lines
3.3 KiB
Python
92 lines
3.3 KiB
Python
from django.contrib.auth import password_validation
|
|
from rest_framework import serializers
|
|
|
|
from .models import Follow, User
|
|
|
|
|
|
class UserSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = User
|
|
fields = ["id", "username", "display_name", "bio", "avatar", "is_private", "date_joined"]
|
|
read_only_fields = ["id", "date_joined"]
|
|
|
|
|
|
class ProfileSerializer(UserSerializer):
|
|
"""UserSerializer plus the counts/relationship state a profile page
|
|
needs. Kept separate from UserSerializer (used for owner/author fields
|
|
all over the feed) so those hot paths don't pay for two extra COUNT
|
|
queries per embedded user."""
|
|
|
|
follower_count = serializers.SerializerMethodField()
|
|
following_count = serializers.SerializerMethodField()
|
|
viewer_follow_id = serializers.SerializerMethodField()
|
|
viewer_follow_status = serializers.SerializerMethodField()
|
|
|
|
class Meta(UserSerializer.Meta):
|
|
fields = UserSerializer.Meta.fields + [
|
|
"follower_count", "following_count", "viewer_follow_id", "viewer_follow_status",
|
|
]
|
|
|
|
def _viewer_follow(self, obj):
|
|
request = self.context.get("request")
|
|
if not request or not request.user.is_authenticated or request.user.id == obj.id:
|
|
return None
|
|
if not hasattr(obj, "_viewer_follow_cache"):
|
|
obj._viewer_follow_cache = Follow.objects.filter(follower=request.user, following=obj).first()
|
|
return obj._viewer_follow_cache
|
|
|
|
def get_follower_count(self, obj):
|
|
return obj.follower_edges.filter(status=Follow.Status.ACCEPTED).count()
|
|
|
|
def get_following_count(self, obj):
|
|
return obj.following_edges.filter(status=Follow.Status.ACCEPTED).count()
|
|
|
|
def get_viewer_follow_id(self, obj):
|
|
follow = self._viewer_follow(obj)
|
|
return follow.id if follow else None
|
|
|
|
def get_viewer_follow_status(self, obj):
|
|
follow = self._viewer_follow(obj)
|
|
return follow.status if follow else None
|
|
|
|
|
|
class RegisterSerializer(serializers.ModelSerializer):
|
|
password = serializers.CharField(write_only=True)
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = ["id", "email", "username", "password", "display_name"]
|
|
|
|
def validate_password(self, value):
|
|
password_validation.validate_password(value)
|
|
return value
|
|
|
|
def create(self, validated_data):
|
|
password = validated_data.pop("password")
|
|
user = User(**validated_data)
|
|
user.set_password(password)
|
|
user.save()
|
|
return user
|
|
|
|
|
|
class FollowSerializer(serializers.ModelSerializer):
|
|
follower = UserSerializer(read_only=True)
|
|
following = UserSerializer(read_only=True)
|
|
following_id = serializers.PrimaryKeyRelatedField(
|
|
queryset=User.objects.all(), source="following", write_only=True
|
|
)
|
|
|
|
class Meta:
|
|
model = Follow
|
|
fields = ["id", "follower", "following", "following_id", "status", "created_at"]
|
|
read_only_fields = ["id", "follower", "status", "created_at"]
|
|
|
|
def create(self, validated_data):
|
|
follower = self.context["request"].user
|
|
following = validated_data["following"]
|
|
status = Follow.Status.PENDING if following.is_private else Follow.Status.ACCEPTED
|
|
follow, _ = Follow.objects.get_or_create(
|
|
follower=follower, following=following, defaults={"status": status}
|
|
)
|
|
return follow
|