# 함수 기반 뷰 def about(request): return HttpResponse('안녕하세요. AskDjango입니다.') # 클래스 기반 뷰 class AboutView(object): @classmethod def as_view(cls, message): def view_fn(request): return HttpResponse(message) return view_fn # 클래스 기반 뷰를 통해 만들어낸 함수 # about객체는 현재 view_fn함수가 만들어짐. about = AboutView.as_view('안녕하세요. AskDjango입니다.')
지금 쓴 클래스뷰는 위에 함수기반뷰와 동일한 실행
참고) 클래스의 3가지 함수 형식
instance method : instance를 통한 호출. 첫번째 인자로 instance가 자동 지정 (self에 대입)
class method : class를 통한 호출. 첫번째 인자로 Class가 자동 지정 (cls에 대입)
static method : class를 통한 호출. 자동으로 지정되는 인자가 없음. 활용은 class method와 동일
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class Person(object): def __init__(self, name, country): self.name = name self.country = country def say_hello(self): return '안녕. 나는 {}이야. {}에서 왔어'.format(self.name, self.country) @staticmethod def new_korean1(name): return Person(name, 'Korea') @classmethod def new_korean2(cls, name): return cls(name, 'Korea')
출력
1 2 3 4 5 6 7 8 9
>>> tom = Person('Tom', 'SF') >>> tom.say_hello() '안녕. 나는 Tom이야. SF에서 왔어' >>> steve = Person.new_korean1('Steve') >>> steve.say_hello() '안녕. 나는 Steve이야. Korea에서 왔어' >>> lee = Person.new_korean2('Lee') >>> lee.say_hello() '안녕. 나는 Lee이야. Korea에서 왔어'
ClassBasedView(CBV)
CBV의 정체는 FBV를 만들어주는 클래스
as_view 클래스함수를 통해 뷰 함수를 생성
장고 기본 CBV 팩키지 위치 : django.views.generic
CBV는 범용적 (generic) 으로 쓸 뷰
1) FBV코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
포맷이 같은 유형
# myapp/views_fbv.py from django.shortcuts import get_object_or_404, render
# 특정 id의 post detail 뷰 함수 def post_detail(request, id): post = get_object_or_404(Post, id=id) return render(request, 'blog/post_detail.html', { 'post': post, }) # 특정 id의 article detail 뷰 함수 def article_detail(request, id): article = get_object_or_404(Article, id=id) return render(request, 'blog/article_detail.html', { 'article': article, })
# myapp/urls.py from django.views.generic import DetailView from .models import Post, Article from . import views_fbv from . import views_cbv from . import views_cbv2
greeting = greeting_view('Good Day') morning_greeting = greeting_view('Morning to ya') evening_greeting = greeting_view('Evenign to ya')
복잡한 뷰를 여러 함수에 나눠 구현하고, 재사용할 때 필요한 루틴만 재정의하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
def post_edit(request, id): instance = get_object_or_404(Post, id=id) # Case 1) 인스턴스 획득 조건을 커스텀 if request.method == 'POST': form = PostForm(request.POST, request.FILES, instance=instance) if form.is_valid(): post = form.save() return redirect('/success_url/') # Case 2) 이동할 URL 변경 else: form = PostForm() return render(request, 'myapp/post_form.html', {'form': form}) # Case 3) 추가 파라미터 지정 def article_edit(request, id): # 위 post_edit 뷰와 비슷한 루틴인데, 중복을 최소화하고 재사용성을 어떻게 높일 수 있을까요? raise NotImplementedError
class PostEditFormView(EditFormView): model = Post success_url = '/weblog/' template_name = 'blog/post_form.html' post_edit = PostEditFormView.as_view()
각 인스턴스 함수들을 재정의
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from django.shortcuts import resolve_url
class PostEditFormView(EditFormView): model = Post template_name='blog/post_form.html' def get_object(self): pk = self.kwargs['pk'] return get_object_or_404(self.model, id=pk, created_at__year=2017) # 2017년 포스팅 중에서 #field에 publish이나 유저가 슈펴만도가능 def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['dummy_params'] = '더미 인자' #추가로하여 템플릿에서 값가져오기가능 return context def get_success_url(self): return resolve_url('blog:post_detail', self.kwargs['pk']) #함수구현을 통해서 매 다른 경로로 이동가능 post_edit = PostEditFormView.as_view()
정리
각 CBV 동작은 소스코드를 직접 살펴보고 이해해야 합니다.
문서로는 설명하는 데에 한계가 있어요.
파이썬에서 멤버함수 호출순서는 MRO 순서를 따릅니다.
파이썬 차근차근 시작하기 <클래스 상속과 MRO> 에피소드 참고
HTTP 메소드별 처리
다양한 HTTP 메소드
GET #spec : 요청 URI의 정보
POST #spec : 요청 URI에 새로운 정보 보냄
PUT #spec : 요청 URI에 갱신할 정보 보냄 (전체를 교체)
PATCH #spec : 요청 URI에 갱신할 정보 보냄 (일부를 교체)
DELETE #spec : 요청 URI의 리소스를 삭제
HEAD #spec : GET요청에서 body는 제외하고 헤더만 응답 >get요청 할지말지 판단빠르게가능
OPTIONS #spec : 요청 URI에서 사용할 수 있는 Method를 응답
TRACE #spec : 보낸 메시지를 다시 돌려보낸다.
HTTP 메소드별 인스턴스함수 지원
하나의 뷰 (즉 하나의 URL) 에서 다양한 HTTP 메소드를 지원해야할 때
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class View(object): http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] # 중략 def dispatch(self, request, *args, **kwargs): #모든 http메소드에 다쓰임 ''' 본 함수를 통해 각 인스턴스 함수로의 분기를 처리 CBV에서는 모든 HTTP 요청은 dispatch 인스턴스 함수를 통해 처리됩니다. ''' if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) #self이므로 현재요청에대해, get, post에 맞는 인스턴스함수들(요소들 attribute)을 획득시도, 없다면 3번째 인자 return 해라 else: handler = self.http_method_not_allowed return handler(requset, *args, **kwargs)
from django.contrib.auth.decorators import login_required from django.views.generic import TempalteView
class SecretView(TemplateView): template_name = 'myapp/secret.html' view_fn = SecretView.as_view()
secret = login_required(view_fn) # 생성된 함수에 장식자 입히기
CBV) 클래스에 직접 장식자 입히기 #1
CBV에서 모든 요청은 dispatch 함수를 통해 처리되므로, dispatch 멤버함수를 꾸며야 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13
from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator from django.views.generic import TempalteView
#아래코드와 FBV뷰 함수에 장식자 적용은 같은 동작을함
class ProtectedView(TemplateView): template_name = 'myapp/secret.html' @method_decorator(login_required) # 인스턴스 함수를 꾸밀 때 #이렇게 장식해줘야함 def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) protected = ProtectedView.as_view()
CBV) 클래스에 직접 장식자 입히기 #2 (추천)
1 2 3 4 5 6 7 8 9 10 11 12
from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator from django.views.generic import TempalteView
# 클래스도 장식자 지원함. 클래스를 꾸밀 때, 인스턴스 함수명을 지칭 @method_decorator(login_required, name='dispatch') #클래스에서 원하는 함수 클래스이름만 적으면됨
class AnotherProtectedView(TemplateView): template_name = 'myapp/secret.html' another_protected = AnotherProtectedView.as_view()
Mixin과 django-braces
Mixins을 통해 여러 CBV를 한번에 조합
Mixins은 그냥 상속이라고 생각하자
CBV는 재사용성에 초점을 둔 뷰
기존 패턴을 사용할 경우에는 CBV가 단순하지만,
새로운 루틴의 뷰를 구현하실 때에는 FBV를 사용하시는 것이 구현이 더 단순해지실 경우 가 많습니다.
파이썬은 다중상속을 지원합니다.
동일 멤버함수가 여러 CBV에 걸쳐서 구현되었을 경우, MRO (Method Resolution Order) 1 순서대로 호출됩니다. (파이썬 차근차근 시작하기 “클래스 상속과 MRO”참조)
예시(FBV)
1 2 3 4 5 6 7
def home_page(request): # 요청이 들어왔을 때 # 템플릿에서 사용할 인자를 준비하고 context = {} # 템플릿 파일을 선택한 다음 template_name = 'home.html' # HttpResponse응답을 생성 return render(request, template_name, context)