반응형

1. Views of DRF

모델 기반의 viewset을 작성했었다.

from rest_framework import viewsets
from .models import Post
from .serializer import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = 

ViewSet

-> View(CRUD)를 설계하는 가장 간단한 방법

 

 

2. APIView

         -> 장점 : 자신의 마음대로 코드를 작성할 수 있다.

         -> 단점 : CBV의 장점인 중복되는 코드 제거를 하지 못한다.

         

         -> Response : 클라이언트가 요청한 형태로 콘텐츠를 렌더링 해준다.

         -> Status : Http 에서 제공하는 상태 코드는 숫자이지만, REST 프레임워크에서는 
              문자 형태의 식별자를 제공하여 상태를 좀더 잘 구분할 수 있도록 한다.

 

         -> APIView를 상속해서 view를 설계할 때는 
             status 와 Response를 import 하여 Response과정을 작성

         -> APIView를 상속해서 만든 CBV의 내부 함수들은 필요로 하는  HTTP Method 이름으로 갖는다.

         -> 어떠한 status를 받고 response를 전달할지 직접 정하는 것이 apiview를 상속받은 cbv

class SnippetDetail(APIView):
    def get(...):
        ...

    def put(...):
        ...

    def delete(...):
        ...
        
#SnippetDetail 클래스에 필요한 method는 GET, PUT, DELETE

##실습해보기

  • django-admin startproject <project-name>
  • cd <project-name>
  • python manage.py startapp <app-name>
  • settings.py에 App, rest_framework추가
  • models.py에 모델 작성 및 migrate
class Post(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()
  • serializer.py 생성 및 작성
from .models import Post
from rest_framework import serializers


class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = "__all__"
  • views.py 작성
    1) 필요한 모듈 추가(apiview를 상속받은 cbv를 작성하기 위해 아래 모듈 추가)
from CBV.models import Post
from CBV.serializer import PostSerializer
from django.http import Http404
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView

           2)객체들 목록 가져오는 PostList 작성

             - post 메서드는 다수의 post 객체를 반환하는 메서드 

             - 다수의 객체를 직렬화 하기위해서는 PostSerializer 에 many = True를 넘겨야 한다

            - post 메서드는 새로운 객세 생성하는 함수

           - 저장 성공시 상태코드 201(created) / 반환 실패시 400(bad request)반환  

class PostList(APIView):
    def get(self, request):
        posts = Post.objects.all()
        serializer = PostSerializer(posts, many=True)

        return Response(serializer.data)

    def post(self, request):
        serializer = PostSerializer(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)

          3) 객체의 상세 정보를 받는 PostDetail 작성

              - 각각의 함수에 pk 값을 인자로 받는다

              - get_object 함수 : get_object_or_404 함수와 동일한 기능을 한다

              - PostList 에서 작성한 함수들과 비슷한 방식으로 작동

class PostDetail(APIView):
    def get_object(self, pk):
        try:
            return Post.objects.get(pk=pk)
        except Post.DoesNotExist:
            return Http404

    def get(self, request, pk, format=None):
        post = self.get_object(pk)
        serializer = PostSerializer(post)

        return Response(serializer.data)

    def put(self, request, pk, format=None):
        post = self.get_object(pk)
        serializer = PostSerializer(post, 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, pk, format=None):
        post = self.get_object(pk)
        post.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)
  • urls.py 작성하기
    1) App 내부의 urls.py 
        - rest_framework.urlpatterns 의 format_suffix_patterns 모듈 추가 
        - views.py 가져와 <ClassName>.as_view() 형식으로 작성
from django.urls import path, include
from rest_framework.urlpatterns import format_suffix_patterns
from . import views

urlpatterns = [
    path("post", views.PostList.as_view()),
    path("post/<int:pk>", views.PostDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

       2) 프로젝트 폴더의 urls.py 
           - include 를 사용해 앱의 urls.py 를 가져와 PATH 설정

from django.contrib import admin
from django.urls import path, include
import CBV.urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(CBV.urls)),
]

실행하면 -> http://127.0.0.1:8000/post 로 젒고하면 결과가 잘 보인다.

 


3. Mixins

  • APIView를 상속해 CBV 구현시 문제점
    • 여러가지 모델 사용 시 비슷한 논리 코드 반복
      -> 불필요한 반복을 제거하기 위해 mixins 사용 
      -> 상속을 통해 불필요한 반복을 제거 

##실습해보기

1. views.py 작성하기 전

  • django-admin startproject <project-name>
  • cd <project-name>
  • python manage.py startapp <app-name>
  • settings.py에 App, rest_framework추가
  • models.py에 모델 작성 및 migrate

2. views.py 작성

   1) 필요한 모듈 추가하기 

from .models import Post
from serializer import PostSerializer
from rest_framework import generics
from rest_framework import mixins

  2) PostList 클래스 작성

  • apiview를 상속해 작성한 PostList 클래스와 동일한 기능
  • mixins 의 ListModelMixin 과 CreateModelMixin , generics의 GenericAPIView 상속
  • 사용할 모델의 쿼리셋과 Serializer 클래스 등록
  • 객체 목록을 가져오는 GET 메서드는 ListModelMixin 에 구현된 list 함수 사용
  • 객체 생성하는 POST 메서드는 CreateModelMixin 에 구현된 create 함수 사용
class PostList(mixins.ListModelMixin,
               mixins.CreateModelMixin,
               generics.GenericAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

   3) PostDetail 클래스 작성하기  

  • PostList를 작성한 것과 동일하게 작성
  • 각 메서드에 필요한 mixins 모듈 상속하고 , serializer, 쿼리셋 등록 후 각 HTTP Method 함수 작성
class PostDetail(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.destroy(request, *args, **kwargs)

3. urls.py 작성

- 앱 내부에 urls.py 생성 및 작성

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views

urlpatterns = [
    path("post", views.PostList.as_view()),
    path("post/<int:pk>", views.PostDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

- 프로젝트 폴더 내부의 urls.py 에 include 

from django.contrib import admin
from django.urls import path, include
import post.urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(post.urls)),
]

실행하고 http://127.0.0.1:8000/post 에 접속하면 정상 작동한다

 

4. Generic CBV

<분류>

  • 기반 뷰(Base View): 뷰 클래스를 생성하고 다른, 제너릭 뷰의 부모 클래스가 되는 기본 제너릭 뷰
    • view : 최상위 부모 제너릭 뷰 클래스
    • TemplateView : 주어진 템플릿으로 렌더링
    • RedirectView : 주어진 URL로 리다이렉트
  • 제너릭 보기 뷰(Generic Display View): 객체의 목록 또는 하나의 객체 상세 정보를 보여주는 뷰
    • DetailView : 조건에 맞는 하나의 객체 출력
    • ListView : 조건에 맞는 객체 목록 출력
  • 제너릭 수정 뷰(Generic Edit View): 폼을 통해 객체를 생성, 수정, 삭제하는 기능을 제공하는 뷰
    • FormView : 폼이 주어지면 해당 폼 출력
    • CreateView : 객체 생성하는 폼 출력
    • UpdateView : 기존 객체 수정하는 폼 출력
    • DeleteView : 기존 객체 삭제하는 폼 출력
  • 제너릭 날짜 뷰(Generic Date View): 날짜 기반 객체의 연/월/일 페이지로 구분해 보여주는 뷰
    • YearArchiveView : 주어진 연도에 해당하는 객체 출력
    • MonthArchiveView : 주어진 월에 해당하는 객체 출력
    • DayArchiveView : 주어진 날짜에 해당하는 객체 출력
    • TodayArchiveVIew : 오늘 날짜에 해당하는 객체 출력
    • DateDetailView : 주어진 연, 월, 일 PK(OR 슬러그)에 해당하는 객체 출력

<제너릭 뷰 오버라이딩>

  • 속성 변수 오버라이딩
    • model : 기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다.
    • queryset : 기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다. queryset을 사용하면 model 속성은 무시된다.
    • template_name : TemplateView를 포함한 모든 제너릭 뷰에서 사용한다. 템플릿 파일명을 문자열로 지정한다.
    • context_object_name : 뷰에서 템플릿 파일에 전달하는 컨텍스트 변수명을 지정한다.
    • paginate_by  : ListView와 날짜 기반 뷰(예, YearArchiveView)에서 사용한다. 페이징 기능이 활성화 된 경우 페이지당 출력 항목 수를 정수로 지정한다.
    • date_field : 날짜 기반 뷰(예, YearArchiveView)에서 사용한다. 이 필드의 타입은 DateField 또는 DateTimeField이다.
    • form_class : FormView, CreateView, UpdateView에서 폼을 만드는데 사용할 클래스를 지정한다.
    • success_url : FormView, CreateView, UpdateView, DeleteView에서 폼에 대한 처리가 성공한 후 리디이렉트할 URL 주소이다.
  • 메소드 오버라이딩
    • def get_queryset() : 기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다. 디폴트는 queryset 속성을 반환한다. queryset 속성이 지정되지 않은 경우 모델 매니저 클래스의 all() 메소드를 호출해 QuerySet 객체를 생성해 반환한다.
    • def get_context_data(**kwargs) : 뷰에서 템플릿 파일에 넘겨주는 컨텍스트 데이터를 추가하거나 변경하는 목적으로 오버라이딩한다.
    • def form_valid(form)

<모델을 지정하는 방법 3가지>

  1. model 속성 변수 지정
  2. queryset 속성 변수 지정
  3. def get_queryset() 메소드 오버라이딩

##실습

1. views.py 작성하기 전

  1. django-admin startproject <project-name>
  2. cd <project-name>
  3. python manage.py startapp <app-name>
  4. settings.py에 App, rest_framework추가
  5. models.py에 모델 작성 및 migrate
  6. serializer.py생성 및 작성

2. views.py 작성하기

   1) 필요한 모듈 추가하기

       - 사용할 모델, serializer , rest_framework 의 generics 추가

from .models import Post
from .serializer import PostSerializer
from rest_framework import generics

    2) PostList 클래스 작성하기

     -  generics의 ListCreateAPIView 는 mixins에서 사용한 list 함수와 create 함수를 가지고 있는 class 이다.

class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    3) PostDetail 클래스 작성하기

     - genercis의 RetrieveUpdateDestoryAPIView 또한 mixins에서 사용한 함수들과 동일한 기능

class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

3. urls.py 작성하기

- 앱 내부에 urls.py 생성 및 작성

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views

urlpatterns = [
    path("post", views.PostList.as_view()),
    path("post/<int:pk>", views.PostDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

- 프로젝트 폴더 내부의 urls.py 에 include 

from django.contrib import admin
from django.urls import path, include
import post.urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(post.urls)),
]

-> 실행 결과 : http://127.0.0.1:8000/post로 접속하면 잘 작동하는 것을 확인할 수 있다.

 

5. ViewSet

rest_framework의 viewsets.py내부에는 4개의 클래스가 존재한다.

  • ViewSet
  • GenericViewSet
  • ReadOnlyModelViewSet
  • ModelViewSet

모두 간단하게 구성되어 mixins와 GenricViewSet, APIView등을 묶는 역할을 한다.

 

ReadOnlyModelViewSet

- 상속 받은 클래스

  • mixins.RetrieveModelMixin
  • mixins.ListModelMixin
  • GenericViewSet

상속받은 클래스로 보아 해당 viewset 역할은 retrieve 함수와 list 함수 기능을 가지고 있다

- 기능 : 특정 객체 가져오는 역할(retrieve)  / 객체 목록을 가져오는 역할(list)

 

ReadOnly를 수행하는 viewSet 이다

 

아래와 같이 작성하면

기존 postlist, postDetail 클래스의 기능을 따로 작성하지 않아도 ReadOnlyModelViewSet을 상속받아 사용할 수 있다.

from rest_framework import viewsets
from .models import Post
from .serializer import PostSerializer

class PostViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Post.objects.all()
    serializer_class = 

ModelViewSet

모델과 Serializer 을 사용할 건지 등록한다

permission_classes는 action을 수행할 수 있는 권한을 설정하는 변수

  • IsAuthenticatedOrReadOnly : 권한이 없을 경우 ReadOnly
  • IsOwnerOrReadOnly : 소유자가 아닐 경우 ReadOnly
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

class SnippetViewSet(viewsets.ModelViewSet):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = [
        permission.IsAuthenticatedOrReadOnly,
        IsOwnerOrReadOnly,
    ]

    @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

-> customAPI 함수는 default로 GET 메서드로 처리된다. / 다른 메서드로 처리하고 싶다면 action의 format 인자로 지정 가능

 

%action decorator

@는 decorator장식자라고 부른다.
ViewSet을 이용하면 CRUD는 간단하게 구현되나 다른 논리들을 구현할 때
@action를 사용한다. 개발자가 임의로 View를 설계할 수 있도록 해준다.

 

%renderer_classes

Response를 어떤 형식으로 Rendering시킬 것인가를 정의

  • renderer.JSONRenderer
  • renderer.BrowsableAPIRenderer
  • renderer.StaticHTMLRenderer
  • TemplateHTMLRenderer

등 여러가지가 있다. 기본 설정은 JSONRenderer와 BrowsableAPIRenderer다.

 

##실습해보기

viewSet 적용하기

1. views.py 작성하기 전

  • django-admin startproject <project-name>
  • cd <project-name>
  • python manage.py startapp <app-name>
  • settings.py에 App, rest_framework추가
  • models.py에 모델 작성 및 migrate
  • serializer.py생성 및 작성

2. views.py 작성하기

    1) 필요한 모듈 추가

from .models import Post
from .serializer import PostSerializer
from rest_framework import viewsets
from rest_framework import renderers
from rest_framework.decorators import action
from django.http import HttpResponse

    2) ReadOnlyModelViewSet을 상속해 클래스 작성하기

      - viewsets의 ReadOnlyModelViewSet을 상속받아 작성한다. ReadOnlyModelViewSet을 상속받았으므로 읽기만 가능하다.

class PostViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

3. urls.py 작성하기

- post 앱 내부의 urls.py

from rest_framework.routers import DefaultRouter
from django.urls import include, path
from .views import PostViewSet

router = DefaultRouter()
router.register('', PostViewSet)

urlpatterns = [
    path('post', include(router.urls))
]

- 프로젝트 폴더의 urls.py 

from django.contrib import admin
from django.urls import path, include
import post.urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(post.urls)),
]

>>실행결과 

-http://127.0.0.1:8000/로 접속하면 아래와 같은 결과를 확인할 수 있다.
DefaultRouter를 사용했기 때문에 PATH가 ""일때도 화면이 나온다.

 

-http://127.0.0.1:8000/post로 접속하면 사용가능한 HTTP Method가
ReadOnlyModelViewSet을 상속받았으므로 GET밖에 없다.

 

4. ModelViewSet 상속해 클래스 작성하기

viewsets의 ModelViewset 상속받은 PostViewSet 클래스 작성

사용할 모델과 serializer을 등록하는 것은 기존과 동일

추가적으로 @action 데코레이터를 이용해 custom api 작성

get 방식으로 @action Custom API Test를 띄우는 highlight 함수 작성

 

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        return HttpResponse("@action Custom API Test")

post로 처리하고 싶다면 @action(method=['post']) 와 같이 작성

 

>>실행결과

http://127.0.0.1:8000/post로 접속하면 사용가능한 HTTP Method가 GET외에도 POST가 추가된 것을 확인

 

Custom API 테스트

>http://127.0.0.1:8000/post/2/와 같이 Detail페이지로 접속하면 Extra Actions 스크롤 메뉴가 생긴 것을 확인할 수 있다.

스크롤 메뉴에서 highlight 버튼 클릭 시 아래와 같은 결과가 생성된다

6. Router

viewSet 복습

- ReadOnlyModelViewSet

  • retrieve()와 list()의 기능

- ModelViewSet

  • list(), create(), retrieve(), update()
    partial_update(), destroy()의 기능

path 

viewSet을 하나의 path 함수로 처리할 수 있을까?

하나의 path로 ListView, DetailView 의 CRUD가 모두 처리 가능할까??

 

===> 불가능하다 / PATH를 하나로 묶어줄 방법이 필요하다

 

as_view

as_view({'http_method' : '처리할 함수'})

예시

path = PostViewSet.as_view({
    'get': 'retrieve',
    'put': 'update',
    'patch': 'partial_update',
    'delete': 'destroy'
})

urlpatterns = [
    path('', path)
]

router : 관례적인 url 맵핑 단계를 자동으로 구현해주는 방식

 

<사용 방법>

1. rest_framework의 router에서 DefaultRouter 추가

from rest_framework.router import DefaultRouter

2. DefaultRouter 객체 생성 및 등록

-> URL Prefix : 127.0.0.1:8000/post와 같은 URL에서 post부분

router = DefaultRouter()
router.register(<URL Prefix>, <View Logic>)

3.  urlpatterns에 router의 urls 추가

urlpatterns = [
    path('', include(router.urls)),
]
반응형

'Web > Django' 카테고리의 다른 글

[Django_심화]4 - (1)pagination  (0) 2020.03.18
[Django] Instagram 클론 코딩(1)  (0) 2020.03.17
[Django_심화]2 - Rest, Serializer  (0) 2019.11.03
[Django_심화]1 - 준비운동  (0) 2019.11.02
[6주차]Form  (0) 2019.03.06

+ Recent posts