들어가며

이전 포스트에서 DRF에서 CBV 패턴으로 view를 정의하는 법을 정리해보았습니다. 이번에는 CBV 중에서도 “장고 APIView”를 사용해서 CRUD를 구현해보려도 합니다.

1:N 모델에서 CRUD를 하기 위해 게시글 – 댓글 모델에 대한 API를 생성해보겠습니다.

API 설계

Model 정의하기

1:N 모델을 정의하기 위해 ForeignKey를 N에 해당하는 모델에 정의합니다.

from django.db import models

class Articles(models.Model):
    title = models.CharField(max_length=50)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)


class Comments(models.Model):
    article = models.ForeignKey(
        Articles, on_delete=models.CASCADE, related_name="comments")
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

시리얼라이져 정의하기

serializers.ModelSerializer을 상속 받아서 각 모델에 해당하는 시리얼라이져를 작성합니다.

model :

fields:

read_only_fields :

from rest_framework import serializers
from .models import Articles, Comments


class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Articles
        fields = "__all__"

class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comments
        fields = "__all__"
        read_only_fields = ("article",)

URL 라우팅 정의하기

리스트 및 생성에서는 pk값을 받을 필요가 없지만 디테일, 수정, 삭제를 pk 값을 받아야 함으로 varible routing을 이용합니다.

“장고 APIView”는 CBV이기 때문에 view 클래스.as_view() 형식으로 라우팅 경로를 입력해줍니다.

from django.urls import path
from . import views

app_name = "articles"


urlpatterns = [
    path("", views.ArticleListAPIView.as_view(), name="article_list"),
    path("<int:pk>/", views.ArticleDetailAPIView.as_view(), name="article_detail"),
    path(
        "<int:article_pk>/comments/",
        views.CommentListAPIView.as_view(),
        name="comment_list",
    ),
    path("/comment/<int:comment_id>",
        views.CommentDetailAPIView.as_view(),
        name="comment_detail",
        )
]

View 정의하기

편의 상 Comment에 관한 APIView 코드만 가져왔습니다.

urls.py에서 urlpattern을 pk 값을 받는 것과 받지 않는 것으로 나누었었죠. view도 동일하게 pk를 받는지 여부를 기준으로 나누어 view를 정의합니다.

class CommentListAPIView(APIView)

class CommentDetailAPIView(APIView)

class CommentListAPIView(APIView):
    def get(self, request, pk):
        article = get_object_or_404(Articles, pk=pk)
        comments = article.comments.all()
        serializer = CommentSerializer(comments, many=True)
        return Response(serializer.data)

    def post(self, request, pk):
        # 어떤 글인지 url로 받아옴
        article = get_object_or_404(Articles, pk=pk)
        serializer = CommentSerializer(data=request.data)
        # is_valid()에서 article 정보는 빼고 검사하도록 CommentSerializer의 read_only_field 설정되야함
        if serializer.is_valid(raise_exception=True):
            # 댓글을 저장할 때 foreign key로 설정한 article을 save()의 인자로 넘김
            serializer.save(article=article)
            return Response(serializer.data, status=status.HTTP_201_CREATED)


class CommentDetailAPIView(APIView):
    def get_object(self, comment_pk):
        return get_object_or_404(Comments, pk=comment_pk)

    def delete(self, request, comment_pk):
        comment = self.get_object(comment_pk)
        comment.delete()
        data = {"pk": f"{comment_pk} is deleted."}
        return Response(data, status=status.HTTP_200_OK)

    def put(self, request, comment_pk):
        comment = self.get_object(comment_pk)
        serializer = CommentSerializer(comment, data=request.data, partial=True)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data)

API 테스트

테스트를 위한 댓글 seed 생성

django-seed를 사용한 테스트 데이터 생성하기
python manage.py seed articles --number=20
python manage.py seed your_app --number=20 --seeder "your_model" "value"
python manage.py seed articles --number=20 --seeder "Comments" 2

실제로 잘 생성 되었나 DB에서 확인해 봅니다.

댓글 API 테스트

댓글 생성

댓글 조회

새로운 댓글이 추가됨을 확인 할 수 있습니다.

댓글 수정

댓글 삭제

마치며

사실상 form을 배웠다면 다를 게 거의 없다는 걸 알 수 있었습니다.

다만 foreign key 처럼 다른 테이블과 연관관계를 가진 데이터를 입력하기 위해서는 주의해야 되는 점이 2개 있었습니다.

  1. is_valid() 에서 제외시키기 위해서 시리얼라이져 클래스에 read_only_field를 추가해야 된다는 점
  2. 시리얼라이져 save() 메서드를 사용할 때, 외래키 값을 전달하기 위해서 연관 테이블 queryset을 인자로 전달해줘야 한다는 점

연습 시 실수 한 사항

urlpatterns

참고하면 좋은 글

APIView 관련 DRF 공식 문서

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다


목차