앞선 튜토리얼에서는 HttpResonse
, render
를 이용하여 함수형 뷰(Function-Based View) 를 구현했다. 하지만 그 과정에서 코드가 반복되고, 구현이 복잡해지는 것을 볼 수 있었다. **클래스 뷰(Class-Based View)**가 함수형 뷰와 달리 어떻게 구현되고, 어떤 장점이 있는지 살펴 볼 것이다.
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
가장 기초적인 구현 방법이다.
from django.shortcuts import render
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {
'lastest_question_list': latest_question_list,
}
return render(request, 'polls/index.html', context)
render
를 이용하여 구현한 방법이다. 위의 구현 방법보다는 더 간단해지는 것을 확인할 수 있다. template
코드 부분이 없어지고, render로 바로 전달하는 것을 볼 수 있다.
- 뷰는 간단 명료해야 한다.
- 뷰 코드의 양은 적으면 적을수록 좋다.
- 뷰 안에서 같은 코드를 반복적으로 사용하지 않는다.
- 뷰는 프레젠테이션 로직에서 관리하고 비즈니스 로직은 모델에서 처리한다. 매우 특별한 경우에만 폼에서 처리한다.
- 403, 404, 500 에러 핸들링에는 CBV를 이용하지 않고 FBV를 이용한다.
- 믹스인은 간단명료해야 한다.
위의 함수형 뷰에서 반복되는 부분을 패턴화하여서 사용하기 쉽게 추상화 하였다. Generic View는 웹 개발시 자주 사용하는 기능을 장고에서 미리 제공해준다.
from django.views import generic
class IndexView(generic.ListView):
model = Question
위와 같이 최소한 그 뷰가 어떤 모델을 사용할 것인지만 지정해주면 generic view가 모든걸 알아서 해준다. 필요에 따라 각 뷰마다 달라지는 값을 넣어주기만 하면된다. Class-based View를 사용하면 다음과 같은 장점이 있다.
- GET, POST등 HTTP 메소드에 따른 처리 코드 작성시 코드 구조가 깔끔해진다. 가독성이 높아진다.
- 다중상속 같은 객체지향 기법을 활용해 제너릭 뷰, 믹스인 클래스 등을 사용해 코드의 재사용과 개발 생산성을 높여준다.
- 복잡한 구현을 가능하게 해준다.
하지만 클래스형 뷰가 함수형 뷰를 완전히 대체하지는 않는다. 상황에 따라 선택하여 사용하는 것이지 어느 한쪽이 더 좋다고 말하기는 어렵다.
Generic View는 from django.views import generic
외부 모듈 적용이 필요하며 다음과 같이 분류할 수 있다.
- Base View : 뷰 클래스를 생성하고 다른 제너릭 뷰의 부모 클래스가 되는 기본 뷰
- View : 최상위 부모 제너릭 뷰 클래스
- TemplateView : 주어진 템플릿으로 렌더링
- RedirectView : 주어진 URL로 redirect
- Genreic Display View : 객체의 목록 또는 하나의 객체 상세 정보를 보여주는 뷰
- DetailView : 조건에 맞는 하나의 객체 출력
- ListView : 조건에 맞는 객체 목록 출력
- Generic Edit View : Form을 통해 객체를 생성, 수정, 삭제하는 기능을 제공하는 뷰
- FormView : 폼이 주어지면 해당 폼을 출력
- CreateView : 객체를 생성하는 폼 출력
- UpdateView : 기존 객체를 수정하는 폼 출력
- DeleteView : 기존 객체를 삭제하는 폼 출력
- Generic Date View : 날짜 기반 객체의 연/월/일 페이지로 구분해 보여주는 뷰
- YearArchiveView: 주어진 연도에 해당하는 객체 출력
- MonthArchiveView: 주어진 월에 해당하는 객체 출력
- DayArchiveView: 주어진 날짜에 해당하는 객체 출력
- TodayArchiveView: 오늘 날짜에 해당하는 객체 출력
- DateDetailView: 주어진 연, 월, 일 PK(또는 슬러그)에 해당하는 객체 출력
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.views import generic
# from django.template import loader
# from django.http import Http404
from .models import Question
# Create your views here.
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Question.objects.order_by('-pub_date')[:6]
"""기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다.
디폴트는 queryset 속성을 반환한다.
queryset 속성이 지정되지 않은 경우 모델 매니저 클래스의 all() 메소드를 호 출해 QuerySet 객체를 생성해 반환한다."""
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
def vote(request, question_id):
question = get_object_or_404(Question, pk = question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', {'question' : question, 'error_message':"You didn't select a choice"})
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))