(Django) Class Based View

duo2208·2022년 1월 3일
0

Django

목록 보기
8/23
post-thumbnail

CBV


함수 기반 뷰에서는 뷰 함수 자체가 내장 함수이고, 클래스 기반 뷰에서는 뷰 클래스가 내장 함수를 반환하는 as_view() 클래스 메서드를 제공합니다.

🦝 django.views.generic.View 에서 해당 메커니즘이 구현되며,
모든 클래스 기반 뷰는 모체 View 클래스를 직간접적으로 상속 및 오버라이딩하여 이용합니다.

ex) CBV 사용 예시

AboutView 클래스는 CBV의 모체인 View 클래스를 상속받습니다.
View 클래스에는 as_view 메서드와 dispatch() 메서드가 정의되어 있습니다.

# urls.py
# CBV를 사용할 때의 URLconf
from django.urls import path
from django.views.generic import AboutView

urlpatterns = [
	path('about/', AboutView.as_view()),
]
# views.py
# AboutView 정의
from django.http import HttpResponse
from django.views.generic import View

class AboutView(View):
	# 뷰 로직 작성
	return HttpResponse('result')
🚀 (Django) Built-in class-based generic views



Built in CBV API : generic.View


  • Base views : 뷰 클래스를 생성하고, 다른 제네릭 뷰의 부모 클래스를 제공하는 기본 제네릭 뷰.
    ›› View, TemplateView, RedirectView
  • Generic display views : 객체의 리스트를 보여주거나, 특정 객체의 상세 정보를 보여줌.
    ›› DetailView, ListView
  • Generic date views : 폼을 통해 객체를 생성, 수정, 삭제하는 기능을 제공.
    ›› ArchiveIndexView, YearArchiveView, MonthArchiveView , WeekArchiveView, DayArchiveView, TodayArchiveView, DateDetailView
  • Generic editing views : 날짜 기반 개겣의 연/월/일 페이지로 구분해서 보여줌.
    ›› FormView, CreateView, UpdateView, DeleteView


ex) DetailView 사용 예시

# urls.py
from . import views

urlpatterns = [
	path('post/<int:pk>/, post_detail),
    	path('article/<int:pk>/, article_detail),
]
# views.py
from django.views.generic import DetailView

post_detail = DetailView.as_view(model=Post)
article_detail = DetailView.as_view(model=Article)
🚀 (Django) Built-in class-based views API



CBV Overriding


제네릭 클래스 기반 뷰의 장점은 단순화를 희생해서 얻은 결과입니다. 제네릭 클래스 기반 뷰는 최대 여덟 개의 슈퍼 클래스가 상속되기도 하는 복합적인 상속체이므로, 커스텀마이징할 때 django.views.generic.View 의 관례에 대해 이해하며 써야합니다.

+ CBV.as_view(**initkwargs) 동작방식

모든 cbv의 모체인 View를 직접 쓸 일은 거의 없습니다. 대신 주요 기능을 http method별로 지정 이름의 멤버 함수를 호출토록 구현합니다.

새로운 View를 생성할 때 CBV.as_view(**initkwargs) 를 통해 생성하는데, 이 때 initkwargs인자는 CBV 생성자로 그대로 전달됩니다.

# Base views - View : 모든 cbv의 모체
class View:
    """
    Intentionally simple parent class for all views. Only implements
    dispatch-by-method and simple sanity checking.
    """

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    def __init__(self, **kwargs):
        """
        Constructor. Called in the URLconf; can contain helpful extra
        keyword arguments, and other things.
        """
        # Go through keyword arguments, and either save their values to our
        # instance, or raise an error.
        for key, value in kwargs.items():
            setattr(self, key, value)

    @classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

    def http_method_not_allowed(self, request, *args, **kwargs):
        logger.warning(
            'Method Not Allowed (%s): %s', request.method, request.path,
            extra={'status_code': 405, 'request': request}
        )
        return HttpResponseNotAllowed(self._allowed_methods())

    def options(self, request, *args, **kwargs):
        """Handle responding to requests for the OPTIONS HTTP verb."""
        response = HttpResponse()
        response['Allow'] = ', '.join(self._allowed_methods())
        response['Content-Length'] = '0'
        return response

    def _allowed_methods(self):
        return [m.upper() for m in self.http_method_names if hasattr(self, m)]

진입 메서드 as_view()의 요청처리 순서

  • as_view() 진입 메서드는 클래스의 인스턴스를 생성합니다.
  • 생성된 인스턴스의 dispatch() 메서드를 호출합니다.
  • dispatch() 메서드는 요청을 검사해여 GET, POST 등의 어떤 HTTP 메서드로 요청되었는지를 알아냅니다.
  • 인스턴스 내에서 해당 이름을 갖는 메서드로 요청을 중계해줍니다.
  • 만일 해당 메서드가 정의되어 있지 않으면 HttpResponseNotAllowed Exception을 방생시킵니다.

+ Mixin

믹스인이란 실체화(instantiation) 된 클래스가 아니라 상속해 줄 기능들을 제공하는 클래스를 의미합니다. 다중 상속을 해야 할 때 믹스인을 쓰면 클래스에 더 나은 기능을 제공할 수 있습니다. 믹스인을 이용하여 뷰 클래스를 제작할 때는 다음과 같은 상속 규칙을 따릅시다.

  • 장고가 제공하는 기본 뷰는 항상 오른쪽으로 진행된다.
  • 믹스인은 기본 뷰에서부터 왼쪽으로 진행한다.
  • 믹스인은 파이썬의 기본 객체 타입을 상속해야만 한다.
from django.views.generic import TemplateView
  
class FreshFruitMixin(object):	# object 객체 타입을 상속한다.
	def get_context_data(self, **kwargs):
  		...
  		return context
 
# FreshFruitMixin 과 TemplateView 를 둘다 상속하지만
# 규칙에 의해 TemplateView는 가장 오른쪽에 위치하고, Mixin은 왼쪽에 둔다.
class FruitFlavorView(FreshFruitMixin, TemplateView):
  	template_name = "fruit_flavor.html"

📌 참고 출처

0개의 댓글