📘 DRF 


DRF with Relationship

Relationship이 있는 데이터를 직렬화(Serialization)하여 제공하는 방법을 학습해 보자

 

댓글 생성을 위한 사전 작업

1. Comment 모델을 작성

더보기
class Comment(models.Model):
    # 1. 외래 키로, 댓글이 어느 글(Article)에 속하는지 알려줌
    # 2. Article 모델과 Comment 모델이 1:N의 관계를 가짐
    # 3. on_delete=models.CASCADE -> Article이 삭제되면, 댓글도 함께 삭제
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True) # 댓글이 처음 생성됐을 때의 시간 기록
    updated_at = models.DateTimeField(auto_now=True) # 댓글이 수정될 때마다 시간 기록

 

2. Migrate

더보기
python manage.py makemigrations
python manage.py migrate

 

3. CommentSerializer 작성

더보기
from .models import Comment

class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = "__all__"

 

4. 🌱 20개의 랜덤 한 데이터를 생성 (seed)

더보기
python manage.py seed articles --number=20

먼저 생각해보기 🤔 

어떤 API가 필요할까?

Name Method Endpoint
특정 Article의 댓글 조회 GET /articles/<int:pk>/comments/
새로운 댓글 작성 POST /articles/<int:pk>/comments/
댓글 수정 PUT /articles/comments/<int:comment_pk>/
댓글 삭제 DELETE /articles/comments/<int:comment_pk>/

1. 특정 Article의 댓글 조회

1.1. articles/urls.py

더보기
# 특정 Article의 댓글 조회
    path("<int:article_pk>/comments/",
         views.CommentListAPIView.as_view(),
         name="comment_list"),

 

1.2. articles/models.py

더보기
변경 전

⬇️

변경 후
class Comment(models.Model):
    # 1. 외래 키로, 댓글이 어느 글(Article)에 속하는지 알려줌
    # 2. Article 모델과 Comment 모델이 1:N의 관계를 가짐
    # 3. on_delete=models.CASCADE -> Article이 삭제되면, 댓글도 함께 삭제
    article = models.ForeignKey(
        Article, on_delete=models.CASCADE, related_name="comments") # 매니저 이름 "comments"로 해주기
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True) # 댓글이 처음 생성됐을 때의 시간 기록
    updated_at = models.DateTimeField(auto_now=True) # 댓글이 수정될 때마다 시간 기록

 

매니저 이름이 생겼어요!

article = models.ForeignKey(
        Article, on_delete=models.CASCADE, related_name="comments") # 매니저 이름 "comments"로 해주기

 

migrations과 migrate 해주세요!

 

1.3. articles/views.py

더보기
from .serializers import CommentSerializer

class CommentListAPIView(APIView):
    def get(self, request, article_pk):
        # Article 모델에서 찾아와요. 근데 Article 모델에는 comment가 없잖아요?(역참조) ⬇️
        article = get_object_or_404(Article, pk=article_pk)
        # 그래서 Comment 모델의 매니저인 "comments"를 데리고 와서, 해당 글에 있는 comments들을 데려와요 ⬇️
        comments = article.comments.all() # 조회된 comments들을
        serializer = CommentSerializer(comments, many=True) # serializer에 넣어줬어요. 많으니까 many=True
        return Response(serializer.data)

 

1.4.🌱 특정 모델에 데이터 넣기 (seed)

🔗 Using with command

더보기
python manage.py seed articles --number=20 --seeder "Comment.article_id" 1

 

1번 Article에 20개의 댓글을 생성했어요

 

1.5. postman


2. 새로운 댓글 작성

2.1. articles/views.py/CommentListAPIView

더보기
class CommentListAPIView(APIView):

def post(self, request, article_pk):
        article = get_object_or_404(Article, pk=article_pk) # 역참조
        serializer = CommentSerializer(data=request.data) # data에 POST 받은 값을 넣어줘요, 여기엔 content 값만 있어요
        if serializer.is_valid(raise_exception=True): # 값이 유효하다면, 예외 발생 True
            serializer.save(article=article) # 저장할 때, 필요한 나머지 데이터를 article로 채워줌
            return Response(serializer.data, status=status.HTTP_201_CREATED)

 

2.2. articles/serializer.py/CommentSerializer

  • read_only_fields
    • serializer.is_valid 를 통과하지 못 한다.
    • 이럴 때는 read_only_fields 를 설정해서 특정 필드를 직렬화 로직에 포함하지 않고, 반환 값에만 포함하도록 할 수 있다.
      • → serializer 입장에서는 내가 넘겨받은 데이터에 article 정보가 없기 때문!
더보기
class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = "__all__"
        read_only_fields = ("article",)

 

2.3. postman


3. 댓글 삭제

3.1. articles/urls.py

더보기
# comment 삭제하기
    path("comments/<int:comment_pk>/",
         views.CommentDetailAPIView.as_view(),
         name="comment_detail"),

 

3.2. articles/views.py

더보기
class CommentDetailAPIView(APIView):
    # 댓글 삭제하기
    def delete(self, request, comment_pk):
        comment = get_object_or_404(Comment, pk=comment_pk) # Comment 모델을 통해서 comment 들고 와요
        comment.delete() # 가져온 comment를 삭제해주고,
        return Response(status=status.HTTP_204_NO_CONTENT) # 204를 보여주면 삭제 끝

 

3.3. postman

더보기

 

삭제 됐다!

 

 

댓글을 조회해 봤더니 41번 댓글이 없어졌다!


4. 댓글 수정

4.1. articles/views.py

더보기
class CommentDetailAPIView(APIView):

# 댓글 수정하기
    def put(self, request, comment_pk):
        comment = get_object_or_404(Comment, pk=comment_pk)
        serializer = CommentSerializer(comment, data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data)

 

4.2. postman

 

4.1. articles/views.py ➡️ 좀 더 편리한 코드

더보기
변경 전

⬇️

변경 후
class CommentDetailAPIView(APIView):
    # 공통된 get_object를 수정하기 편하게끔 함수 만들어주기
    def get_object(self, comment_pk):
        return get_object_or_404(Comment, pk=comment_pk)
    
    # 댓글 삭제하기
    def delete(self, request, comment_pk):
        comment = self.get_object(comment_pk) # Comment 모델을 통해서 comment 들고 와요
        comment.delete() # 가져온 comment를 삭제해주고,
        return Response(status=status.HTTP_204_NO_CONTENT) # 204를 보여주면 삭제 끝
    
    # 댓글 수정하기
    def put(self, request, comment_pk):
        comment = self.get_object(comment_pk)
        serializer = CommentSerializer(comment, data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data)

 

  1. get_object_or_404(Comment, pk=comment_pk) ➡️ self.get_object(comment_pk)
  2. def get_object 추가
    # 공통된 get_object를 수정하기 편하게끔 함수 만들어주기
    def get_object(self, comment_pk):
        return get_object_or_404(Comment, pk=comment_pk)

 

이 코드를 추가했어요!

 

Postman 정리하기

더보기

 

Add folder를 눌러주세요

 

 

생성해 주신 폴더에 API들 넣어주세요

 

 

끝!


 

+ Recent posts