drf에서 게시글의 상세 페이지 기능을 구현하고, 포스트맨으로 테스트를 했더니 아래와 같이 출력되었다.
class ArticleDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = "__all__"
우선 ForeignKey로 가져온 user는 pk 값으로 조회되어 SerializerMethodField를 사용하여 nickname 값으로 변경해주었고, category 역시 ForeignKey라서 pk 값이 출력되어 related_name를 입력 후 StringRelatedField를 사용하여 str 값으로 변경했다.
게시글 상세 페이지 안에 댓글들도 가져오면 별도로 댓글 전체를 보여주는 View가 별도로 필요 없을 것 같아
댓글 전체를 가져오는 Serializer를 만들고, 게시글 상세 페이지의 Serializer에 related_name으로 넣어주었다.
이 때 many=True를 걸어주지 않아서 에러를 찾는데 엄청 오래 걸렸다.
# models.py
class Article(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='article_user')
category = models.ForeignKey(Hobby, on_delete=models.CASCADE, related_name='article_category')
title = models.CharField(max_length=500)
content = models.TextField()
article_image = models.ImageField(upload_to='media/article/', null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.title)
class Comment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comment_article')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comment_user')
content = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.content)
# views.py / 게시글 상세 페이지 조회
class ArticleDetailView(APIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get(self, request, article_id):
article = get_object_or_404(Article, id=article_id)
serializer = ArticleDetailSerializer(article)
return Response(serializer.data, status=status.HTTP_200_OK)
# serializers.py
# 댓글 전체 리스트 Serializer
class CommentListSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField()
def get_user(self,obj):
return obj.user.nickname
class Meta:
model = Comment
fields = "__all__"
# 게시글 상세 보기 Serializer
class ArticleDetailSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField()
category = serializers.StringRelatedField()
# CommentListSerializer를 사용하려면 실행 순서의 영향을 받기 때문에 ArticleDetailSerializer보다 위에서 정의해야 한다.
comment_article = CommentListSerializer(many=True)
def get_user(self, obj):
return obj.user.nickname
class Meta:
model = Article
fields = "__all__"
Serializer를 다루면서 시행착오가 많았는데 우선 자주 쓰이는 SerializerMethodField로 커스텀 필드를 만들 수 있다.
SerializerMethodField 는 read-only field 로 값을 저장하거나 수정할 때는 사용되지 않고 조회할 때만 사용한다.
사용 방법은 위의 코드에서 보이듯이 커스텀할 필드를 SerializerMethodField로 정의하고, get_<field_name>로 커스텀 할 필드를 넣으면 obj는 해당 모델의 obj가 들어온다. 그리고 변경하고 싶은 obj의 필드를 입력해주면 적용된 필드 값이 출력된다.
그리고 ForeignKey로 연결된 필드를 조회했을 때 기본적으로 pk 값이 출력되는데 FK로 연결된 모델에서 __str__method를 사용하는 경우 StringRelatedField를 사용하여 해당 모델의 str 메소드의 인스턴스 값을 가져온다.
특정 모델을 역참조하는 필드를 Serializer에 가져오고 싶을 때는 related_name으로 역참조된 모델로 생성된 Serializer를 입력해주면 역참조한 모델로 생성된 객체를 가져온다.
(예시로 위의 코드 중 게시글 상세보기 Serializer 참고! comment_article = CommentListSerializer(many=True)
Serializer 모델에 대입하는 인자는 하나의 객체 인스턴스여야 한다. 따라서 queryset이나, 객체 리스트를 넣으면 에러가 발생한다. 이때, 쿼리 셋이나 객체로 생성된 리스트를 하용하는 인자가 many=True이다.