- 아래의 코드는 python2.7 , restframework 3.6.4 의 내용입니다. 최신버전의 내용과 다른 부분이 있습니다.
1.APIView
APIView -> 클래스 기반 (CBV : class base view)
@api_view -> 함수기반 (FBV : function base view)
APIView는 여러가지 기본 설정을 제공합니다. ( 직렬화, 인증, 사용량 제한, 권한 등 )
CVB로 작성시 http 메소드에 해당하는 함수를 만들어줘야 합니다. 해당 함수 명은 지정되어 있으며, http의 메소드 명과 동일합니다. (함수명이 틀릴 경우 해당 메소드는 사용할수 없는 메소드로 간주 합니다. )
url에는 파라미터인자를 선언하므로 인자 pk를 받아야 하는 get/put/delete가 하나의 클래스, 파라미터가 필요 없는 gets/post가 하나의 클래스로 만든후 각각의 url로 선언 해야 합니다.
- url과 views의 클래스가 두개로 나눠진 이유는 pk를 파라미터로 받기 위함입니다. 물론 router()로 url로 설정하시면 하나로 할수 있습니다. (router에 대한 설명은 하단에 있습니다. )
# urls.py
url(r'^post/(?P<pk>[\d+]+)/', views.PostDetailAPIView.as_view()),
url(r'^post/', views.PostAPIView.as_view()),
# views.py
from django.http import JsonResponse
from rest_framework.views import APIView
class PostDetailAPIView(APIView):
def get(self, request, pk):
return JsonResponse({}, status=status.HTTP_200_OK)
def put(self, request, pk):
return JsonResponse({}, status=status.HTTP_200_OK)
def delete(self, request, pk):
return JsonResponse({}, status=status.HTTP_200_OK)
class PostAPIView(APIView):
def get(self, request):
return JsonResponse({}, status=status.HTTP_200_OK)
def post(self, request):
return JsonResponse({}, status=status.HTTP_200_OK)
2. Mixins
APIView는 메소드마다 쿼리와 시리얼라이져를 선언해줘야했습니다.
쿼리와 시리얼라이져의 중복이 많으므로 공통된 것을 하나로 묶기 위해서 mixins를 사용합니다.
각각의 믹스인은 메소드에 해당합니다.
RetrieveModelMixin : Get
ListModelMixin : Gets
CreateModelMixin : Post
UpdateModelMixin : Put
DestroyModelMixin : delete
PostListMixins 클래스는 하위파라미터 없이 실행할수 있는 gets/post를
PostDetailMixins 클래스는 pk를 받아 처리하는 get/put/delete를 각각의 믹스인으로 묶은것이다. (pk를 받는 메소드와 받지 않는 메소드)
# urls.py
url(r'^post/(?P<pk>[\d+]+)/', views.PostDetailMixins.as_view()),
url(r'^post/', views.PostListMixins.as_view()),
# views.py
from rest_framework.response import Response
from rest_framework import generics
from rest_framework import mixins
from .models import Post
from .serializers import PostSerializer
class PostListMixins(mixins.ListModelMixin, mixins.CreateModelMixin,generics.GenericAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
def get(self, request, *args, **kwargs):
return self.list(request)
def post(self, request, *args, **kwargs):
return self.create(request)
class PostDetailMixins(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
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.delete(request, *args, **kwargs)
3. Generics APIView
Mixin은 하나의 클래스에 너무 많은 상속을 합니다. 공통된것끼리 더 줄이기 위해서 generics를 사용합니다.
generics.CreateAPIView : 생성
generics.ListAPIView : 목록
generics.RetrieveAPIView : 조회
generics.DestroyAPIView : 삭제
generics.UpdateAPIView : 수정
generics.RetrieveUpdateAPIView : 조회/수정
generics.RetrieveDestroyAPIView : 조회/삭제
generics.ListCreateAPIView : 목록/생성
generics.RetrieveUpdateDestroyAPIView : 조회/수정/삭제
# urls.py
url(r'^post/(?P<pk>[\d+]+)/', views.PostDetailGeneric.as_view()),
url(r'^post/', views.PostListGeneric.as_view()),
# views.py
from rest_framework import generics
from .models import Post
from .serializers import PostSerializer
class PostListGeneric(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
class PostDetailGeneric(generics.RetrieveUpdateDestroyAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
4. Viewset
Generics APIView를 통해 많은 부분이 간소화 되었지만 그래도 query set / serializer을 파라미터(Pk)에 따라 두개의 클래스를 선언해 줘야 합니다.
이것도 귀찬으니 하나로 합시다!!
viewsets.ReadOnlyModelViewSet : 목록 조회, 특정 레코드 조회 (get만 가능)
viewsets.ModelViewSet : 목록 조회, 특정 레코드 생성/조회/수정/삭제 전부 자동으로 만들어줌.
Viewsets.ViewSet : 자동으로 만들지마! 내가 다 만들께
# views.py
from .models import Post
from .serializers import PostSerializer
from rest_framework import viewsets
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
하지만 viewset을 이용하려면 url을 Router()를 사용해야 합니다.
만일 .as_set()를 사용하면 메소드 하나하나 선언해 줘야 합니다.
# urls.py
from rest_framework.routers import DefaultRouter
post_router = DefaultRouter()
post_router.register('viewset',views.PostViewSet)
urlpatterns = [
path('',include(post_router.urls)),
]
번외) view에 따라 url을 어떻게 선언해야 하는가?
- 아래의 코드는 python2.7 , restframework 3.6.4 의 내용입니다. 최신버전의 내용과 다른 부분이 있습니다.
위에서 봤던 viewset과 APIView는 각각 상위 상속이 다르기 때문에 url의 선언도 약간식 차이가 있습니다.
url에서 선언방식 as_view / router 와 view에서 클래스 선언방식 viewset / APIView 총 4가지 조합 방식에 대해 알아봅시다.
1. Url as_view() + view viewset
.as_view()에 해당 method를 지정 해야 줘야 작동합니다. (함수의 이름은 커스텀이 당연히 가능합니다)
-> views.MoreUsersView.as_view({'get': 'get', 'put':'update’})
https://www.django-rest-framework.org/api-guide/viewsets/
url(r'^more/(?P<user_id>[\d+]+)/', views.MoreUsersView.as_view({"get": "get", "put": "put"}), name='more'),
class MoreUsersView(viewsets.ViewSet):
queryset = UniqueUser.objects.all()
def get(self, request, user_id):
return JsonResponse({}, status=status.HTTP_200_OK)
def put(self, request, user_id=None):
return JsonResponse({}, status=status.HTTP_200_OK)
2. Url as_view + view APIView
APIView 일 경우엔 view안의 함수 명이 메소드 명과 일치해야 합니다.
그렇지 않으면 해당 메소드를 사용할 수 없습니다. Put 요청일 경우
def update(self, request, pk): <- 동작 안함
def put(self, request, pk): <- 동작
당연히 리스트의 경우 url이 다르므로(pk를 파라미터로 받지 않으므로) url을 따로 지정 해줘야 합니다.
url(r'^more/(?P<user_id>[\d+]+)/', views.MoreUsersView.as_view(), name='more'),
class MoreUsersView(APIView):
queryset = User.objects.all()
def get(self, request, user_id):
return JsonResponse({}, status=status.HTTP_200_OK)
def put(self, request, user_id=None):
return JsonResponse({}, status=status.HTTP_200_OK)
View 함수 이름 커스텀은 @api_view()로 CBV가 아닌 FBV로 해야 합니다.
url(r'^more/(?P[\d+]+)/', views.get_put_custom, name='more-get’),
@api_view(['GET', 'PUT'])
def get_put_custom(request, user_id):
return JsonResponse({}, status=status.HTTP_200_OK)
3. Url router + view viewset
# urls.py
more_router = DefaultRouter()
more_router.register(r'', views.MoreUsersView)
url(r'^more/', include(more_router.urls)),
# views.py
class MoreUsersView(viewsets.ViewSet):
# get
def retrieve(self, request, pk):
return JsonResponse({}, status=status.HTTP_200_OK)
# put
def update(self, request, pk):
return JsonResponse({}, status=status.HTTP_200_OK)
Router + ViewSet일 경우엔 view 내의 함수 이름을 router에서 동작하는 함수명으로만 해야 합니다.
이름이 틀리면 해당 함수는 동작하지 않습니다. (함수명 확인 https://www.django-rest-framework.org/api-guide/routers/#defaultrouter)
만일 정말!! 함수명을 변경하고 싶다면 rest_framework.decorators의 detail_route / list_route를 써야 하며, (최신 버전에서는 action)
from rest_framework.decorators import detail_route
class MoreUsersView(viewsets.ViewSet):
@detail_route(methods=['post', 'delete', 'put'])
def post_del_put_custom(self, request, pk=None):
return JsonResponse({}, status=status.HTTP_200_OK)
접근시 url이 다음과 같아야 한다.
url/{pk}/함수명/
예시 http://localhost/more/2/post_del_put_custom/
마지막에 작성한 함수명의 이름이 url에 들어가야 접근이 가능합니다.
아래의 명세를 참조하세요.
https://www.django-rest-framework.org/api-guide/routers/#defaultrouter
https://www.django-rest-framework.org/api-guide/viewsets/
4. Url router + view APIView
router는 viewset에서만 동작합니다. APIView에서는 동작하지 않습니다.
번외
Router() 로 url을 설정할 경우 id는 무조건 pk로 받아야 합니다. (아니면 에러가 난다)
router에서는 해당 아이디를 pk로 오버라이딩 하며, 뷰에서는 무조건 해당 파라미터로 받아야 하기 때문입니다.
# urls.py
more_router = DefaultRouter()
more_router.register(r'', views.MoreUsersView)
url(r'^more/', include(more_router.urls)),
# views.py
class MoreUsersView(viewsets.ViewSet):
def get(self, request, pk):
print("pass", pk)
'web > Django_rest_framework' 카테고리의 다른 글
[Django rest framework] 1. 기존 셋팅 + CRUD (0) | 2020.06.04 |
---|---|
[Django rest framework] 튜토리얼을 시작하며. (0) | 2020.06.02 |
django rest_freamework에서 리턴값을 json으로 보내자. (0) | 2020.03.27 |
django에서 swagger로 한다면 drf_yasg 는 왠만하면.... (0) | 2019.05.20 |
django REST_FRAMEWORK login (0) | 2019.01.11 |