Class 기반의 View
이전에 구현했던 것처럼 HTTP 메서드를 기반으로 View를 생성(GET, POST, PUT, DELETE) 할 수도 있지만, 클래스를 기반으로 View를 생성할 수도 있다. 먼저 전체 리스트를 불러오거나(GET) 데이터를 생성(POST)했던 QuestionList가 어떻게 바뀌었는지 보자.
from rest_framework.response import Response
from rest_framework import status
from polls.models import Question
from .serializers import QuestionSerializer
# APIView 사용을 위한 라이브러리 임포트
from rest_framework.views import APIView
# 데코레이터가 아닌 각 메서드를 함수로 선언
class QuestionList(APIView):
def get(self, request):
questions = Question.objects.all()
serializer = QuestionSerializer(questions, many=True)
return Response(serializer.data)
def post(self, request):
serializer = QuestionSerializer(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)
함수 위에 데코레이터로 선언을 한 뒤 if문을 통해 각 메서드를 수행했던 이전 방식과 달리, QuestionList라는 클래스 안에 각 메서드를 함수로 지정해주었다. 이 경우 request가 어떻게 요청되느냐(request 형식에 따라)에 따라 get 함수를 실행할지 post 함수를 실행할지 자동으로 선택한다.
각 함수가 행동하는 방식은 이전 코드와 동일하게 작동한다. 서버를 열어 새로고침해도 이전과 똑같은 화면이 노출되며 POST도 동일하게 동작한다.
다음은 인스턴스 하나씩을 불러오는 상세 view의 PUT, DELETE를 클래스 기반 view로 구현해보자.
class QuestionDetail(APIView):
def get(self, request, id):
try:
question = Question.objects.get(pk=id)
except Question.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = QuestionSerializer(question)
return Response(serializer.data)
def put(self, request, id):
try:
question = Question.objects.get(pk=id)
except Question.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = QuestionSerializer(question, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
try:
question = Question.objects.get(pk=id)
except Question.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
question.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
위와 동일하게 각 메서드를 함수로 구현했다. try, except 문을 통해 인스턴스가 없을 때 404 에러코드를 반환한 뒤, 각 메서드에 맞는 행동을 실행한다.
urls.py 수정
클래스 기반 view를 새로 생성했으니, urls.py에서 해당 URL에서 실행할 함수도 새롭게 수정해주어야 한다.
from django.urls import path
from . import views
app_name = 'polls_api'
urlpatterns = [
path('question/', views.QuestionList.as_view(), name='question_list'),
path('question/<int:id>/', views.QuestionDetail.as_view(), name='question_detail'),
]
views.{각 클래스}.as_view()를 사용하면 해당 클래스를 지칭하는 의미가 된다.
Mixin
데코레이터 활용을 통한 함수를 사용하는 것보다 클래스 기반 view를 사용했을 때 더 코드가 간결해진다는 것을 알 수 있었다. 하지만 이 보다 더 간편한 방법이 있다. Mixin과 GenericAPIView를 이용하면, 클래스를 상속받을 수 있어 세세한 함수 코드를 따로 작성하지 않아도 된다.
from rest_framework.response import Response
# mixins, generics 임포트
from rest_framework import status, mixins, generics
from polls.models import Question
from .serializers import QuestionSerializer
# 3가지를 파라미터로 받음
class QuestionList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
# 모델 클래스 지정
queryset = Question.objects.all()
# 시리얼라이저 클래스 지정
serializer_class = QuestionSerializer
# ListModelMixin의 list 메서드 사용
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
# CreateModelMixin의 create 메서드 사용
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
위 코드를 살펴봤을 때, get과 post 함수가 매우 간단해진 것을 알 수 있다. 각 요청에 따라 실행할 함수가 자동으로 지정되며, Mixin과 GenericAPIView를 통해 미리 구현된 메서드를 활용하면 되기 때문이다.
이어서 QeustionDetail 클래스도 이어서 살펴보자.
class QuestionDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
QuestionDetail 클래스에서는 인자를 4개 받는데, genericAPIView를 제외하면 위에 쓰임과 모두 다르다. mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin까지 총 3가지인데, 각각 GET(특정 메서드만 조회), PUT, DELETE 메서드와 관련된 것이다.
또한 urls.py에서 약간의 수정이 필요하다.
from django.urls import path
from . import views
app_name = 'polls_api'
urlpatterns = [
path('question/', views.QuestionList.as_view(), name='question_list'),
# int:id --> int:pk
path('question/<int:pk>/', views.QuestionDetail.as_view(), name='question_detail'),
]
genericAPIView를 사용하기 위해 int:id를 int:pk로 바꿔주었다. GenericAPIView에서는 pk로 detail한 값을 찾기 때문이다.
대부분의 모델에서 GET, POST, PUT, DELETE는 반복되는 동작이므로 Mixin과 GenericAPIView를 이용하면 손쉽게 구현할 수 있다.
Generic API View
위 코드에서도 Generic API View를 사용했지만, generics안에 있는 view를 활용한다면 이전보다 훨씬 더 간단한 view 코드를 생성할 수 있게 된다.
from rest_framework import status, mixins, generics
from polls.models import Question
from .serializers import QuestionSerializer
class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
위에서 구현한 class 2개 (QuestionList, QuestionDetail)가 이렇게 간단하게 구현된다. get,post 등 메서드에 대한 함수 구현이 전혀 필요 없어진 것이다.
List는 ListCreateAPIView를, Detail은 RetrieveUpdateDestroyAPIView를 사용하는데, 각 클래스에 이미 각 메서드에 대한 기능이 구현되어 있기 때문에 가능한 일이다.
'Minding's Programming > Django' 카테고리의 다른 글
[Django] RelatedField (0) | 2024.10.11 |
---|---|
[Django] User 항목 모델에 추가 / User 관리 / User생성 / User 권한 (2) | 2024.10.10 |
[Django] GET, POST, PUT, DELETE (1) | 2024.10.10 |
[Django] Serilaizer (4) | 2024.10.10 |
[Django] Admin 페이지 편집 (0) | 2024.10.10 |