Django Generic View

앞선 튜토리얼에서는 HttpResonse, render를 이용하여 함수형 뷰(Function-Based View) 를 구현했다. 하지만 그 과정에서 코드가 반복되고, 구현이 복잡해지는 것을 볼 수 있었다. 클래스 뷰(Class-Based View)가 함수형 뷰와 달리 어떻게 구현되고, 어떤 장점이 있는지 살펴 볼 것이다.

Function-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로 바로 전달하는 것을 볼 수 있다.

Class-based View

Class-Based View 사용 가이드라인

  • 뷰는 간단 명료해야 한다.

  • 뷰 코드의 양은 적으면 적을수록 좋다.

  • 뷰 안에서 같은 코드를 반복적으로 사용하지 않는다.

  • 뷰는 프레젠테이션 로직에서 관리하고 비즈니스 로직은 모델에서 처리한다. 매우 특별한 경우에만 폼에서 처리한다.

  • 403, 404, 500 에러 핸들링에는 CBV를 이용하지 않고 FBV를 이용한다.

  • 믹스인은 간단명료해야 한다.

Generic Views

위의 함수형 뷰에서 반복되는 부분을 패턴화하여서 사용하기 쉽게 추상화 하였다. 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,)))

참고

Last updated