Django는 클래스 기반 뷰(class-based views)라는 함수가 아닌 파이썬 객체를 이용하여 뷰를 생성하는 방법도 제공합니다. 클래스 기반 뷰는 함수 기반 뷰와는 달리 다음 장점을 갖고 있습니다.
그리고 뷰 개발에서 흔히 발견되는 관용구와 패턴을 추상화하고 일반적인 경우에 뷰 개발을 용이하게 하기 위해 자주 쓰는 클래스 기반 뷰들을 미리 만들어서 제공하고 있는데, 이 뷰를 제네릭 뷰
라고 합니다.
Django에서 제공하는 클래스 기반 뷰 및 제네릭 뷰에 대한 자세한 정보는 아래 링크를 참고바랍니다.
Built-in class-based views API
자 그러면 지금까지 만든 코드에 이것을 한 번 적용해봅시다.
먼저, 기존에 함수 기반 뷰로 작성된 test_app/views.py파일의 해당 부분을 수정해봅시다.
...
def index(request):
latest_book_list = TestBook.objects.order_by("-reg_datetime")[:5]
context = {"latest_book_list" : latest_book_list,}
return render(request, "book/index.html", context)
def book(request, book_id):
book = get_object_or_404(TestBook, pk=book_id)
return render(request, "book/detail.html", {"book": book, "range":range(10)})
def comment(request, book_id):
...
아래는 클래스 기반 뷰로 변경한 형태입니다.
...
from django.views.generic import (
ListView,
DetailView,
)
class BookIndexView(ListView):
template_name = "book/index.html"
context_object_name = "latest_book_list"
def get_queryset(self):
return TestBook.objects.order_by("-reg_datetime")[:5]
class BookDetailView(DetailView):
model = TestBook
template_name = "book/detail.html"
context_object_name = 'book'
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
context_data = super().get_context_data(**kwargs)
context_data['range'] = range(10)
return context_data
def comment(request, book_id):
...
기존 index()
함수는 BookIndexView
클래스로 전환되었으며 BookIndexView
는 목록의 형태로 데이터를 보여주기 때문에 ListView
라는 제네릭뷰를 상속받도록 설정합니다.
Python 언어에서의 상속은 아래와 같은 형태로 상속관계를 설정할 수 있습니다.
class 클래스명(상속받을 부모클래스명)
함수의 입력 파라미터와 형태가 비슷해 보이나 전혀 다른 개념이므로 혼동되지 않도록 합니다.
그리고 필요한 값들을 미리 정해둔 변수값에 입력받는 형식으로 작성을 하게 됩니다. BookIndexView
예시는 template_name
변수에 사용자 화면에 바탕이 될 템플릿의 이름(파일경로)를 작성하고, context_object_name
값을 설정하여 화면에 목록으로 보여질 모델의 목록 객체의 이름을 'latest_book_list'로 선언했습니다.
그리고 화면에 뿌려질 모델을 가져오기 위한 메서드인 get_queryset()
함수는 기존에 모델을 최근 5개까지의 모델들을 목록으로 리턴하도록 재정의(override)하였습니다.
목록을 보여주는 뷰 뿐만 아니라 실제 다른 제네릭 뷰를 사용하더라도 위 예시와 같이 용도에 맞는 제네릭 뷰
클래스를 상속받게 설정한 후 부모클래스에서 미리 정의된 멤버변수에 필요한 값들을 채워 넣고 필요한 메서드를 재정의(override)하는 형식으로 구현된다고 보시면 됩니다.
기존 book 함수도 BookDetailView
클래스로 전환되었으며, 마찬가지로 제네릭 뷰의 DetailView
를 상속받아서 구현되었습니다.
model
변수에 모델로 사용할 테이블 모델 클래스를 지정하고, template_name
과 context_object_name
에 각각 화면에 노출시킬 템플릿 명과 그 템플릿을 이용하여 화면에 그릴 때 필요한 모델 객체 데이터를 담을 변수의 이름을 선언했습니다.
또 context_data
라는 딕셔너리 객체에 range
라는 키를 추가하여 템플릿에서 평점 입력을 위한 라디오 버튼을 생성할 때 필요한 값을 함께 리턴하도록 재정의(override)하였습니다.
참고로 context_object_name
은 별도로 설정하지 않으면 기본값으로 모델 객체가 내려가게 됩니다. 하지만 기본값을 사용하지 않고 수정한 이유는 만들어 두었던 템플릿 파일을 그대로 활용할 수 있게 하기 위함입니다.
예를 들어 기존에 작성해뒀던 templates/book/index.html
를 한 번 살펴봅시다.
<h3>신간도서</h3>
{% if latest_book_list %}
<ul class="list-group">
{% for book in latest_book_list %}
<li>
<a href="{% url 'bookstore:detail' book.id %}">{{ book.title }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No books are available.</p>
{% endif %}
views.py에서 context_object_name
을 이미 기존 템플릿 파일내에서 사용하는 이름으로 바꿔주었기 때문에 템플릿 파일들은 별도 수정없이 그대로 사용할 수 있게 되었습니다.
context_object_name
의 기본값은 제네릭 뷰가 상속받는 Mixin에 따라 이름이 결정됩니다. 예를 들어 제네릭 뷰가SingleObjectMixin
을 상속받는 단일 개체를 위한 뷰라면 모델명이 곧 이름이 됩니다. 하지만ListView
와 같이 복수 개체를 위한 뷰라면MultipleObjectMixin
을 상속받기 때문에 모델명 뒤에 '_list'라는 이름으로 디폴트 값이 정해지게 됩니다.
ex)
Book 모델 단일 개체용 뷰에서의 기본값 : book
Book 모델 복수 개체용 뷰에서의 기본값 : book_list
믹스인(Mixin) 이란?
Mixin
은 python에서 제공하는 다중상속을 활용한 클래스의 한 종류입니다. 하지만 일반적인 클래스 상속의 개념과는 다르게 재사용되는 코드의 파편, 즉 스스로는 별 의미를 가지지 않지만 상속받는 클래스에게 부가적인 기능을 추가시켜주는 용도로 사용하는 클래스입니다.
다음으로 만들어진 클래스 기반뷰로 라우팅 되도록 test_app/urls.py
파일도 수정해 줍니다.
...
from . import views
app_name = "bookstore"
urlpatterns = [
path("", views.BookIndexView.as_view(), name="index"),
path("<int:pk>/", views.BookDetailView.as_view(), name="detail"),
...
]
정보에 감사드립니다.