DRF는 같은 API를 점점 더 적은 코드로 쓰게 해 주는 뷰 계층을 제공한다. 함수 뷰 → APIView → 제네릭 뷰(generic view)와 믹스인(mixin) → 구체 제네릭 뷰 → ViewSet과 Router 순으로 올라갈수록 관례가 많아지고 코드가 줄어든다.
단계별
@api_view(["GET", "POST"]) # 1. 함수 뷰
def movie_list(request): ...
class MovieList(APIView): # 2. 메서드별 핸들러
def get(self, request): ...
def post(self, request): ...
class MovieList(generics.ListCreateAPIView): # 3. 구체 제네릭 뷰
queryset = Movie.objects.all()
serializer_class = MovieSerializer
class MovieViewSet(viewsets.ModelViewSet): # 4. ViewSet: 목록·생성·조회·수정·삭제 전부
queryset = Movie.objects.all()
serializer_class = MovieSerializer
router = DefaultRouter()
router.register("movies", MovieViewSet) # URL을 자동 생성
urlpatterns = router.urls- DRF의
Request는request.data로 JSON·폼 본문을 똑같이 읽게 해 주고,Response는 콘텐츠 협상(content negotiation)으로 형식을 정한다(DRF 파서·렌더러와 콘텐츠 협상) GenericAPIView는get_queryset()·get_object()·페이징·필터 같은 공통 기능을,CreateModelMixin같은 믹스인은create()·list()같은 동작을 준다. 이를 묶은 것이ListCreateAPIView,RetrieveUpdateDestroyAPIView같은 구체 뷰다- View와 ViewSet: View는 HTTP 메서드(get·post)별로, ViewSet은 동작(list·create·retrieve·update·destroy)별로 메서드를 나눈다. ViewSet은 Router와 함께 URL을 자동으로 만든다
ReadOnlyModelViewSet은 조회만 연다
자주 덮어쓰는 지점
class ReviewList(generics.ListCreateAPIView):
serializer_class = ReviewSerializer
def get_queryset(self): # URL의 pk로 범위를 좁힘
return Review.objects.filter(watchlist_id=self.kwargs["pk"])
def perform_create(self, serializer): # 저장할 때 요청 사용자 넣기
serializer.save(author=self.request.user)queryset만 두면 URL과 상관없이 전체가 나온다. 중첩 URL에서는get_queryset으로 좁힌다- ViewSet에 기본 CRUD 외 동작은
@action(detail=True)으로 추가한다 - URL이 관례에서 벗어나면 Router에 억지로 맞추기보다 뷰를 직접 연결하는 편이 낫다
관례 위에 올라탈수록 코드는 짧아지지만, 무엇이 자동으로 일어나는지 모르면 동작을 추적하기 어렵다(누수 추상화의 법칙). 권한·필터·페이지는 DRF 인증과 권한, DRF 필터·검색·정렬, 페이지네이션 (offset·cursor)을 본다. 반복을 줄이는 도구의 한계는 DRY 원칙와 3의 법칙도 함께 볼 만하다.
출처: DRF Generic views: GenericAPIView · Mixins · Concrete View Classes · DRF ViewSets · Extra actions