# articles/views.py
from .models import Article, Comment
from .serializers import ArticleListSerializer, ArticleSerializer, CommentSerializer
@api_view(['GET'])
def comment_list(request):
comments = Comment.objects.all()
serializer = CommentSerializer(comments, many=True)
return Response(serializer.data)
# articles/views.py
@api_view(['GET'])
def comment_detail(request, comment_pk):
# 특정 댓글 데이터를 조회
comment = Comment.objects.get(pk=comment_pk)
serializer = CommentSerializer(comment)
return Response(serializer.data)
# articles/views.py
@api_view(['POST'])
def comment_create(request, article_pk):
# 어떤 게시글에 작성되는 댓글인지 단일 게시글을 조회
article = Article.objects.get(pk=article_pk)
serializer = CommentSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save(article=article)
return Response(serializer.data, status=status.HTTP_201_CREATED)
위 코드만으로는 400 Bad Request가 발생한다.
원인: CommentSerializer에서 외래 키에 해당하는 article 필드 또한 사용자로부터 입력받도록 설정되어 있기 때문에, 서버 측에서는 해당 필드가 누락되었다고 판단함.
해결: 유효성 검사 목록에서 제외 → article 필드를 읽기 전용 필드로 설정
# articles/serializers.py
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
read_only_fields = ('article',)
# articles/views.py
@api_view(['GET', 'PUT', 'DELETE'])
def comment_detail(request, comment_pk):
comment = Comment.objects.get(pk=comment_pk)
if request.method == 'GET':
serializer = CommentSerializer(comment)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = CommentSerializer(comment, data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
elif request.method == 'DELETE':
comment.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
댓글 목록 조회 시
article의 pk 뿐만 아니라 게시글 제목(title)까지 함께 응답하기
CommentSerializer 내부에 ArticleTitleSerializer를 별도로 정의하여 article의 title만 뽑아오도록 구현한다.
# articles/serializers.py
class CommentSerializer(serializers.ModelSerializer):
class ArticleTitleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ('title',)
article = ArticleTitleSerializer(read_only=True)
class Meta:
model = Comment
fields = '__all__'
# ❌ read_only_fields = ('article',)
# 특정 필드를 재정의 또는 추가한 경우 read_only_fields는 작동하지 않음
# → 새로운 필드에 read_only 키워드 인자로 직접 작성해야 함
Article → Comment 간 역참조 구성을 활용하여 JSON 데이터를 재구성해보자.
# articles/serializers.py
class ArticleSerializer(serializers.ModelSerializer):
class CommentDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ('id', 'content',)
comment_set = CommentDetailSerializer(many=True, read_only=True)
class Meta:
model = Article
fields = '__all__'
기본적으로 게시글을 조회하면 참조 중인 댓글의 개수는 알 수 없다. Article 모델에 댓글 개수를 저장하는 필드를 지정하지 않았기 때문에, 직접 계산해야 한다.
→ annotate 사용
# articles/views.py
from django.db.models import Count
@api_view(['GET', 'DELETE', 'PUT'])
def article_detail(request, article_pk):
article = Article.objects.annotate(num_of_comments=Count('comment')).get(pk=article_pk)
# articles/serializers.py
class ArticleSerializer(serializers.ModelSerializer):
...
num_of_comments = serializers.SerializerMethodField()
class Meta:
...
def get_num_of_comments(self, obj):
# obj는 Serializer가 처리하는 Article 인스턴스
# view에서 annotate 한 필드를 그대로 사용 가능
return obj.num_of_comments
obj는 현재 직렬화 중인 모델 인스턴스이며, 이 메서드에서obj의 속성이나annotate된 필드를 활용해 새 값을 만들 수 있음.
Serializer에서 추가적인 데이터 가공을 하고 싶을 때 사용. (비율, 합계, 평균 등)
SerializerMethodField를 Serializer 클래스 내에서 필드로 선언하면, DRF는 get_<필드명>이라는 이름을 가진 메서드를 자동으로 찾음full_name = serializers.SerializerMethodField()라고 선언하면, DRF는 get_full_name(self, obj) 메서드를 찾아 해당 값을 직렬화 결과에 넣어 줌class UserSerializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('id', 'username', 'full_name', 'email',)
def get_full_name(self, obj):
return f'{obj.first_name} {obj.last_name}'
get_ 메서드는 반드시 (self, obj) 형태로 정의해야 하며, obj는 현재 직렬화 중인 모델 인스턴스를 의미| 목적 | 설명 |
|---|---|
| 유연성 | 다양한 계산 로직을 손쉽게 추가 가능 |
| 가독성 | 데이터 변환 과정을 Serializer 내부 메서드로 명확히 분리 |
| 유지보수성 | view나 model에 비해 Serializer 측 로직 변경이 용이 |
| 일관성 | view에서 별도로 data 수정 없이도 직렬화 결과를 제어 |