Dev/Web Programming

DRF Views 정리

dragonhyeon 2024. 11. 25. 18:29
728x90
반응형

Function-Based Views vs Class-Based Views

Function-Based Views (FBV) :

  • 뷰를 단순한 함수로 정의
  • 간단한 작업을 처리하기에 적합하며, 코드가 짧고 직관적
from rest_framework.decorators import api_view
from rest_framework.response import Response


@api_view(['GET'])
def hello_world(request):
    return Response({'message': 'Hello, World!'})

Class-Based Views (CBV) :

  • 뷰를 클래스로 정의하며, DRF 의 APIView 나 Generic Views 등을 상속받아 사용
  • 복잡한 작업을 처리하거나 재사용성을 높이고자 할 때 적합
from rest_framework.views import APIView
from rest_framework.response import Response


class HelloWorldAPIView(APIView):
    def get(self, request):
        return Response({'message': 'Hello, World!'})

 

FBV 는 규모가 작고 간단한 API 엔드포인트를 빠르게 구현할 때 적합하며 CBV 는 규모가 크고 복잡한 API 엔드포인트를 구현할 때, 재사용성과 확장성이 중요할 때, 그리고 DRF의 기능 (ViewSet, Generic Views 등) 을 최대한 활용하고 싶을 때 적합합니다. 이러한 이유로 DRF 에서는 CBV 를 사용하는 것이 더 일반적입니다.

APIView vs Generic View vs ModelViewSet

APIView: 기본 제공 기능 없이 모든 것을 직접 구현

  • 특징: 최소한의 추상화를 제공. 모든 HTTP 메서드 (get, post, put, delete 등) 를 직접 구현해야 함
  • 장점: 높은 커스터마이징 가능성
  • 단점: 기본 CRUD 기능도 직접 구현해야 하므로 코드량이 많아질 수 있음
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from .models import Item
from .serializers import ItemSerializer


class ItemAPIView(APIView):
    def get(self, request):
        items = Item.objects.all()
        serializer = ItemSerializer(items, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = ItemSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Generic View: 기본 CRUD 작업 중 일부를 제공

  • 특징: RESTful 작업 중 일부를 미리 구현한 클래스
  • 장점: 반복 작업을 줄이고 CRUD 기능을 빠르게 생성 가능
  • 단점: 특정 HTTP 메서드 (예: delete 등) 를 추가로 구현하려면 커스터마이징 필요
from rest_framework.generics import ListCreateAPIView

from .models import Item
from .serializers import ItemSerializer


class ItemListCreateView(ListCreateAPIView):
    queryset = Item.objects.all()
    serializer_class = ItemSerializer
  • queryset, serializer_class 를 지정하는 것만으로 기본 CRUD 기능을 자동으로 제공
  • 주요 Generic Views:
    • ListCreateAPIView
      • GET: 객체 목록 조회
      • POST: 객체 생성
    • RetrieveUpdateDestroyAPIView
      • GET: 단일 객체 조회
      • PUT / PATCH: 객체 수정
      • DELETE: 객체 삭제
    • ListAPIView
      • GET: 객체 목록 조회
    • CreateAPIView
      • POST: 객체 생성
    • RetrieveAPIView
      • GET: 단일 객체 조회
    • UpdateAPIView
      • PUT / PATCH: 객체 수정
    • DestroyAPIView
      • DELETE: 객체 삭제

ModelViewSet: CRUD 전체를 미리 구현하고 URL 라우팅까지 자동화

  • 특징: CRUD 작업을 모두 포함하며, URL 라우팅까지 자동화
  • 장점: 가장 간단하게 CRUD 기능을 제공하며, 코드량을 크게 줄임
  • 단점: 지나치게 추상화되어 있어 세부 커스터마이징이 필요할 때는 오히려 불편
# app_name/views.py
from rest_framework.viewsets import ModelViewSet

from .models import BlogPost
from .serializers import BlogPostSerializer


class BlogPostViewSet(ModelViewSet):
    queryset = BlogPost.objects.all()
    serializer_class = BlogPostSerializer
  • queryset, serializer_class 를 지정하는 것만으로 기본 CRUD 기능을 자동으로 제공
# app_name/urls.py
from rest_framework.routers import DefaultRouter

from .views import BlogPostViewSet

router = DefaultRouter()
router.register(r'blogposts', BlogPostViewSet, basename='blogpost')

urlpatterns = router.urls
  • 위처럼 작성하면, 아래와 같은 CRUD URL이 자동 생성:
    • GET (목록 조회) : /blogposts/
    • POST (새 객체 생성) : /blogposts/
    • GET (특정 객체 조회) : /blogposts/<pk>/
    • PUT (특정 객체 수정) : /blogposts/<pk>/
    • PATCH (특정 객체 부분 수정) : /blogposts/<pk>/
    • DELETE (특정 객체 삭제) : /blogposts/<pk>/

Generic View vs ModelViewSet 구현

특징 Generic View ModelViewSet
URL 정의 방식 직접 각각의 경로 정의 Router 를 사용해 URL 라우팅 자동 생성
URL 커스터마이징 세밀하게 커스터마이징 가능 기본 CRUD 패턴 자동화, 세밀한 라우팅은 추가 작업 필요
코드량 더 많은 코드 작성 필요 라우팅 자동화로 코드량 적음
로직 추가 및 커스터마이징 새로운 뷰 클래스를 생성하거나 기존 클래스를 확장 메서드 오버라이드 또는 @action 사용
적합한 상황 복잡한 URL 구조, 특정 HTTP 메서드별 상세 로직 필요 시 단순 CRUD 중심의 API 구현 시

Generic View

# app_name/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status, generics


class BlogPostHighlightView(APIView):
    def post(self, request, pk, *args, **kwargs):
        try:
            post = BlogPost.objects.get(pk=pk)
            post.is_highlighted = True
            post.save()
            return Response({'message': 'Post highlighted successfully'}, status=status.HTTP_200_OK)
        except BlogPost.DoesNotExist:
            return Response({'error': 'Post not found'}, status=status.HTTP_404_NOT_FOUND)


class BlogPostListCreateView(generics.ListCreateAPIView):
    queryset = BlogPost.objects.all()
    serializer_class = BlogPostSerializer
# app_name/urls.py
from django.urls import path

from .views import BlogPostListCreateView, BlogPostHighlightView

urlpatterns = [
    path('posts/', BlogPostListCreateView.as_view(), name='blogpost-list-create'),
    path('posts/<int:pk>/highlight/', BlogPostHighlightView.as_view(), name='blogpost-highlight'),
]

ModelViewSet

# app_name/views.py
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status, viewsets


class BlogPostViewSet(viewsets.ModelViewSet):
    queryset = BlogPost.objects.all()
    serializer_class = BlogPostSerializer

    @action(detail=True, methods=['post'])
    def highlight(self, request, pk=None):
        post = self.get_object()
        post.is_highlighted = True
        post.save()
        return Response({'message': 'Post highlighted successfully'}, status=status.HTTP_200_OK)
# app_name/urls.py
from rest_framework.routers import DefaultRouter

from .views import BlogPostViewSet

router = DefaultRouter()
router.register(r'posts', BlogPostViewSet, basename='blogpost')
urlpatterns = router.urls
728x90
반응형