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 value
SerializerMethodField
활용:
DB 필드 vs. Serializer 필드:
Nested Serializer 설계:
유효성 검증:
validate_<field_name>
메서드를 사용하여 특정 필드에 대한 검증 로직을 간단하게 추가할 수 있음.prefetch_related
또는 select_related
와 같은 ORM 기능을 활용할 계획.