from rest_framework import serializers
from .models import Review, ReviewComment, ReviewLike
class ReviewCommentSerializer(serializers.ModelSerializer):
"""ReviewComment 모델 직렬화"""
nickname = serializers.SerializerMethodField()
class Meta:
model = ReviewComment
fields = ['id', 'review', 'user', 'nickname', 'content', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
def get_nickname(self, obj):
"""유저 닉네임 반환 (유저가 없으면 '알수없음')"""
return obj.user.nickname if obj.user else "알수없음"
class ReviewLikeSerializer(serializers.ModelSerializer):
"""ReviewLike 모델 직렬화"""
nickname = serializers.SerializerMethodField()
class Meta:
model = ReviewLike
fields = ['id', 'review', 'user', 'nickname', 'is_active', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
def get_nickname(self, obj):
"""유저 닉네임 반환 (유저가 없으면 '알수없음')"""
return obj.user.nickname if obj.user else "알수없음"
class ReviewSerializer(serializers.ModelSerializer):
"""Review 모델 직렬화"""
nickname = serializers.SerializerMethodField()
comments = ReviewCommentSerializer(many=True, read_only=True) # 연결된 댓글들
total_likes = serializers.SerializerMethodField()
total_dislikes = serializers.SerializerMethodField()
class Meta:
model = Review
fields = ['id', 'user', 'nickname', 'content', 'app_id', 'score', 'created_at', 'updated_at', 'comments', 'total_likes', 'total_dislikes']
read_only_fields = ['id', 'created_at', 'updated_at', 'comments', 'total_likes', 'total_dislikes']
def get_nickname(self, obj):
"""유저 닉네임 반환 (유저가 없으면 '알수없음')"""
return obj.user.nickname if obj.user else "알수없음"
def get_total_likes(self, obj):
"""총 좋아요(추천) 수 반환"""
return obj.likes.filter(is_active=1).count()
def get_total_dislikes(self, obj):
"""총 비추천 수 반환"""
return obj.likes.filter(is_active=-1).count()
def validate_score(self, value):
"""
평점 검증: 0.5~5.0 사이의 값만 허용하고, 0.5 단위로 작성되어야 함.
"""
if not (0.5 <= value <= 5.0):
raise serializers.ValidationError("평점은 0.5에서 5.0 사이의 값이어야 합니다.")
if value * 10 % 5 != 0:
raise serializers.ValidationError("평점은 0.5 단위로 작성되어야 합니다.")
return value
오늘 작업한 내용은 Django Rest Framework(DRF) 기반의 serializer.py를 설계하고, Review, ReviewComment, ReviewLike 모델을 직렬화하는 과정에서의 주요 의사결정을 정리했다.
Review, ReviewComment, ReviewLike의 데이터를 직렬화하여 API로 제공.1) nickname 필드
user.nickname 값은 Review나 ReviewComment 모델의 필드가 아니기 때문에, 기본 직렬화 방식으로는 포함 불가능.SerializerMethodField를 활용해 nickname 필드를 동적으로 생성.nickname = serializers.SerializerMethodField()get_nickname 메서드에서 닉네임 값을 반환."알수없음"을 반환해 API의 안정성을 보장.2) comments 필드 (연결된 댓글 목록)
ReviewCommentSerializer를 사용하여 Nested Serializer로 구현.comments = ReviewCommentSerializer(many=True, read_only=True)many=True를 설정하여 여러 개의 댓글을 포함.read_only=True)으로 처리해 댓글 생성/수정을 별도 엔드포인트로 분리.3) total_likes와 total_dislikes 필드
목표: Review가 받은 총 좋아요와 비추천 수를 포함하여 사용자 인터페이스에서 유용한 정보를 제공.
문제: 초기 설계에서는 이 값을 Review 모델 필드로 저장하려고 했으나, 두 가지 문제점이 있었다.
결정: DB에 저장하지 않고 Serializer에서 동적으로 계산.
total_likes = serializers.SerializerMethodField()
total_dislikes = serializers.SerializerMethodField()
get_total_likes와 get_total_dislikes 메서드를 사용해 ReviewLike 모델의 is_active 상태를 기반으로 필드 값 계산.4) score 필드 유효성 검증
validate_score 메서드에서 커스텀 검증 로직 추가.def validate_score(self, value):
if not (0.5 <= value <= 5.0):
raise serializers.ValidationError("평점은 0.5에서 5.0 사이의 값이어야 합니다.")
if value * 10 % 5 != 0:
raise serializers.ValidationError("평점은 0.5 단위로 작성되어야 합니다.")
return valueSerializerMethodField 활용:
DB 필드 vs. Serializer 필드:
Nested Serializer 설계:
유효성 검증:
validate_<field_name> 메서드를 사용하여 특정 필드에 대한 검증 로직을 간단하게 추가할 수 있음.prefetch_related 또는 select_related와 같은 ORM 기능을 활용할 계획.