Django Class Based views (2)
CBV를 좀 더 자세하게 알아보자
제네릭 뷰가 어떻게 구성되어 있는지 !!
Generic View제네릭 뷰
-
반복적으로 사용되는 공통 부분을 패턴화하여 사용하기 쉽게 추상화 해둔 것
-
Base View
- View, TemplateView, RedirectView
- 모든 CBV의 부모 클래스로서 기본적인 기능들이 들어있다
- 직접 쓸 일은 거의 없지만 다른 클래스들을 사용함으로써 간접적으로 항상 사용되고 있다
- 전체 코드 보기
View
- 최상위 부모 제네릭 뷰 클래스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
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)]
- init
- as_view에 인자를 넘겨주게 되면 그 인자가
*kwargs
로 넘어와 이 dict 를 순회하며 해당 설정을 해주게 된다. 예를 들어template_name
값을 지정했다고 하면 인스턴스 변수를 생성하여 이에 값을 할당하고 해당 변수가 필요할 경우 클래스 변수보다 인스턴스 변수를 먼저 참조한다.
- as_view에 인자를 넘겨주게 되면 그 인자가
- as_view
- view를 하나 만들어서 이를 return 한다. 클래스를 정의한 후에 이에 해당하는 함수를 만들어 그 함수를 사용하는 방식으로 동작하기 때문에 CBV도 결국 기본적인 동작은 함수를 베이스로 한다.
- dispatch
- as_view 메소드 내부에서 호출된다. request method에 들어있는 method가 정의된 http_method_names에 해당된다면 request에서 이를 가져오게되고, 정상적인 method가 아니라면
http_method_not_allowed
를 호출하게 된다.
- as_view 메소드 내부에서 호출된다. request method에 들어있는 method가 정의된 http_method_names에 해당된다면 request에서 이를 가져오게되고, 정상적인 method가 아니라면
TemplateView
- 주어진 템플릿으로 렌더링
1 2 3 4 5 6 7
class TemplateView(TemplateResponseMixin, ContextMixin, View): """ Render a template. Pass keyword arguments from the URLconf to the context. """ def get(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) return self.render_to_response(context)
- TemplateView는
TemplateResponseMixin
,ContextMixin
,View
3개의 class를 상속받는다 -
기본 View는 항상 맨 오른쪽에 위치하며, Mixin들은 View의 왼쪽에 위치하게 된다
-
TemplateResponseMixin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
class TemplateResponseMixin: """A mixin that can be used to render a template.""" """Template의 기본적인 설정들""" template_name = None template_engine = None response_class = TemplateResponse content_type = None def render_to_response(self, context, **response_kwargs): """ Return a response, using the `response_class` for this view, with a template rendered with the given context. Pass response_kwargs to the constructor of the response class. rendering하는 과정 """ response_kwargs.setdefault('content_type', self.content_type) return self.response_class( request=self.request, template=self.get_template_names(), context=context, using=self.template_engine, **response_kwargs ) def get_template_names(self): """ Return a list of template names to be used for the request. Must return a list. May not be called if render_to_response() is overridden. template의 이름을 설정 """ if self.template_name is None: raise ImproperlyConfigured( "TemplateResponseMixin requires either a definition of " "'template_name' or an implementation of 'get_template_names()'") else: return [self.template_name]
- TemplateResponseMixin은 template의 기본적인 설정, template의 이름, rendering 하는 과정들을 담당한다.
-
ContextMixin
1 2 3 4 5 6 7 8 9 10 11 12
class ContextMixin: """ A default context mixin that passes the keyword arguments received by get_context_data() as the template context. """ extra_context = None def get_context_data(self, **kwargs): kwargs.setdefault('view', self) if self.extra_context is not None: kwargs.update(self.extra_context) return kwargs
- context를 받아서 처리해주는 함수
- 정적인 context에 대해서는 extra_context 변수를 사용해서 처리
- 동적인 경우에는 get_context_data함수를 오버라이딩해서 사용한다
- TemplateView를 상속한 CBV의 예
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# CBV from django.views.generic import TemplateView from articles.models import Article # 동적인 context index = TemplateView.as_view( template_name = "index.html", extra_context = {'one': 1}, ) # 정적인 context (eg. request.user) class MyTemplateView(TemplateView): template_name = "index.html" # 오버라이딩 def get_context_data(self, **kwargs): # 기본 구현 호출 context = super().get_context_data(**kwargs) # Article의 QuerySet을 더한다 context['latest_articles'] = Article.objects.all()[:5] # QuerySet이 아닌 다르게 더하기 context.update({ 'one': 1, 'userName' : self.request.user, }) return context
RedirectView
- 주어진 URL로 리다이렉트
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
class RedirectView(View): """Provide a redirect on any GET request.""" permanent = False url = None pattern_name = None query_string = False def get_redirect_url(self, *args, **kwargs): """ Return the URL redirect to. Keyword arguments from the URL pattern match generating the redirect request are provided as kwargs to this method. """ if self.url: url = self.url % kwargs elif self.pattern_name: url = reverse(self.pattern_name, args=args, kwargs=kwargs) else: return None args = self.request.META.get('QUERY_STRING', '') if args and self.query_string: url = "%s?%s" % (url, args) return url def get(self, request, *args, **kwargs): url = self.get_redirect_url(*args, **kwargs) if url: if self.permanent: return HttpResponsePermanentRedirect(url) else: return HttpResponseRedirect(url) else: logger.warning( 'Gone: %s', request.path, extra={'status_code': 410, 'request': request} ) return HttpResponseGone() def head(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def options(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def patch(self, request, *args, **kwargs): return self.get(request, *args, **kwargs)
- get
- get_redirect_url을 호출하여 url을 얻는다.
- url을 얻지 못하면 에러 출력, url을 제대로 얻었다면 permanent에 의해 분기되지만 결과적으로 redirect된다
- get_redirect_url
- 클래스 변수 url을 통해 직접 url을 받아오거나 pattern_name을 통해 url의 name 값을 받아왔으면 이를 return
- 추가로 parment나 query_string을 지정해줄 수 있다
- head, post, options … patch
- 모두 get 함수를 실행시킨다
참고사이트