DRF 가이드

이주환·2023년 11월 10일

파이썬 냉장고

목록 보기
3/3

공식문서 외 활용 할 수 있는 사이트


Django REST Framework 3.14 -- Classy DRF

Django Class-Based-View Inspector -- Classy CBV

DRF 통신 방식 기본 값


  1. Brower: API
  2. Client: JSON
    • curl, httppie, postman, vue.js, react.js 등이 클라이언트로 접근

기본 인증 방식


REST FRAMEWORK

  • rest_framework.permissions.DjangoModelPermissionOrAnnoReadOnly : 로그인 한 유저는 CRUD 기능을 사용 할 수 있지만, 그 외엔 읽기 기능만 가능하다.
  • 기본 값은 AllowAny로 작동하며 모든 유저에게 CRUD 권한을 준다.

DRF의 일방적인 작업 순서


DRF는 Viewset이란 강력한 조합 무기가 있다. 이 것의 장점으로는 고작 3줄 짜리 코드가 모든 CRUD를 이끌어준다는 것인데 언제 사용 하는게 가장 좋을까?

프로젝트를 처음 시작 할 때 모든 테이블을 있는 그대로 읽기 위한 CRUD가 필요한 상황

이렇게, 명확한 상황에서 뷰셋을 사용하고 요구사항에 맞춰 ListAPIView와 같은 Http Request의 요구조건에 맞는 API View를 커스텀 하면 효율적인 코드 작성 방식이 된다.

라우터


Default Router

  • route_url/ GET(list)

  • route_url/ POST(create)

  • route_url/pk/ GET(retrieve)

  • route_url/pk/ PUT(update)

  • route_url/pk/ DELETE(destory)

  • route_url/pk/ PATCH(partial_update)

  • API Root (route_root_url/)

Viewsets


뷰셋을 알기 전, 상속 개념을 먼저 되짚는다.

ModelViewSet의 상속도

CRUD Views

  • CreateAPIView: Create
  • ListAPIView: Read
  • RetrieveAPIView
  • UpdateAPIView: Update
  • DestoryAPIView: Delete

이후, mixins과 뷰들의 조합을 섞어 2가지 이상의 View를 표현 한 기능도 존재한다.

CRUD ViewSet

← 5가지의 모든 기능을 조합 하여 만듬

Generics View


UpdateAPIView, ListAPIView, CreateAPIView, RetrieveAPIView, DestoryAPIView

이 모든 뷰 모델은 해당 뷰 모델 ← 모델 믹스인을 상속 받는 구조이고, http request에 해당하는 메서드들을 사용하고 있다.

👉 put, get 등

UpdateAPIView - PUT | PATCH


UpdateView에서 Patch는 Partial Update를 사용한다. Partial과 일반 Put과 다른 점이 무엇일까?

Put

Partial

Partial Update는 키워드 아규먼트에 파티알 인자를 True로 변경하고 업데이트를 진행 하는 것이다.

이 둘은 어느 때에 사용 할까?

일반 Put method는 특정한 인자 값이 필수로 존재 해야 하는 경우에 그 인자를 꼭 받아야만 업데이트가 된다.

반대로, 파티알은 그 필수 인자 값을 무시하고 업데이트가 되는 형태이다.

  • 장고 공식문서에서도 필수 옵션이 없어도 업데이트가 가능하다 라고 표현 하고 있다.

3-3 커리큘럼을 듣고 난 후 문득 궁금증이 생겨 지식공유자에게 질문을 남겼다.

  • 위 질문의 맥락은 이렇다. 유저 1명이 1개의 게시글에 대해 좋아요를 누르는데, 이미 눌렀다면 다음에 누르는 좋아요는 취소가 되어야 한다.
  • 어떻게 구현 할 것인가?
  • 모델 필드를 보면 유저를 참조 하고 있지도 않고, 작성자가 누군지 모르는 상황이었기 때문에 요구사항에 맞춰 리팩터링 한다면 어떻게 해야 효율적인 모델링과 쿼리셋을 작성할지 의문이여서 질문의 답변을 참고 해서 코드를 살짝 바꿔봐야겠다.
  • 답변

답변을 받긴 했지만 키워드를 착각 했나보다, 좋아요 기능의 구현인데 아마 불리언 필드를 이용 해서 좋아요를 체크 하는 것보다 N : M field로 유저를 체크 하는게 더 정확할 것 같다. 이 부분은 페이스북 클론 코딩 때 써봐야겠다.

  • 코드 적용 Post/models.py
    class Post(models.Model):
        writer = models.ForeignKey(CustomUser, on_delete=models.CASCADE, help_text="작성자")
        title = models.CharField(max_length=500, help_text="제목")
        content = models.TextField(help_text="내용")
        image = models.ImageField(
            upload_to="post/%Y/%m/", blank=True, null=True, help_text="이미지 업로드"
        )
        like = models.PositiveSmallIntegerField(default=0, help_text="좋아요")
        like_people = models.ManyToManyField(
            CustomUser, related_name="like_people", help_text="좋아요를 누른 사람들"
        )
        created_at = models.DateTimeField(auto_now_add=True, help_text="생성 일자")
        updated_at = models.DateTimeField(auto_now=True, help_text="수정 일자")
    
        class Meta:
            db_table = "post"
            ordering = ["-created_at"]
            verbose_name = "게시글"
            verbose_name_plural = "게시글"
    Post/views.py
    class PostViewSets(viewsets.ModelViewSet):
        queryset = Post.objects.all()
        serializer_class = PostSerializer
    
        @action(detail=True, methods=["patch"], name="like")
        def like(self, request, pk=None):
            """게시물 좋아요 버튼 클릭 했을 때 액션"""
            post = self.get_object()
            user = request.user
    
            if post.like_people.filter(id=user.id).exists():
                post.like_people.remove(user.id)
                post.like -= 1
            else:
                post.like_people.add(user.id)
                post.like += 1
    
            post.save()
            return Response(status=status.HTTP_200_OK)
    
        def create(self, request, *args, **kwargs):
            request_data = request.data.copy()
            request_data["writer"] = request.user.id
            serializer = self.get_serializer(data=request_data)
            serializer.is_valid(raise_exception=True)
            saved_profile = serializer.save(writer=request.user)
    
            return Response(serializer.data, status=status.HTTP_201_CREATED)
    
        def get_serializer_class(self):
            if self.action in ("create", "update"):
                return PostCreateSerializer
    
            return PostSerializer

리팩터링 순서


강의의 마지막을 듣고 이런 생각을 했었다. 그렇다면, 코드 최적화는 어떠한 과정을 거쳐야 장고를 이용했을 때 이점을 톡톡히 활용 했다고 할 수 있을까? API View로 url마다 매핑 하는 것이 효율적일까? 이러한 궁금증을 질문 해봤다.

사실 이에 대한 답변은 그건 취향 차이입니다 라고 해도 무관 했을지언정, 친절한 답변을 받았고 생각도 쉽게 정리되었다.

  • 답변

실제 위와 같은 답변을 받고 나서, DRF는 뷰셋에서 큰 틀을 구성 하고 세분화 하며 단위마다 쪼개는 것이 현명 할 것 같다. 제너릭 뷰는 뷰셋에서 사용되는 Mixin을 이해 하는데 큰 도움이 되고, 기능의 일부 단위만 사용 할 수 있기 때문에 필요한 항목이었다.

  • 업데이트 PUT vs PATCH의 차이점은?
  • GET은 어떤 메서드를 호출 하고 있는가?
  • Retreive와 List의 차이는 무엇일까?

등, 우리가 실제 요구사항에 맞춰 기능을 구현 할 때에는 HTTP 통신 방식에 의해 기능을 정의 하게 되는데 이 때 리트리브, 겟, 디스트로이 등 뷰셋에서 제공하는 메서드들을 오버라이딩 하면 각 통신 방식 마다 다른 시리얼라이저 라든지, 다른 기능을 구현 할 수 있다.

get_serializer의 이해 도우미


[TIL] get_serializer()와 serializer.save()의 instance, get_object_or_404

profile
안녕하새우

0개의 댓글