Two Scoops of Django 3.x를 읽고 정리한 글입니다.
장고는 함수 기반 뷰(function-based views, FBV)와 클래스 기반 뷰(class-based views, CBV)를 지원한다.
어떤 개발자들은 대부분의 뷰를 함수 기반 뷰로 처리한다. 그리고 클래스 기반 뷰는 서브 클래스가 필요한 경우에 대해 제한적으로 이용하기도 한다. 이 또한 문제 될 것이 없다.
from django.urls import path
from django.views.generic import DetailView
from tastings.models import Tasting
urlpatterns = [
path('<int:pk>',
DetailView.as_view(
model=Tasting,
template_name='tastings/detail.html'),
name='detail'),
path('<int:pk>/results/',
DetailView.as_view(
model=Tasting,
template_name='tastings/results.html'),
name='results'),
]
이 코드는 장고의 디자인 철학에 어긋난다.
앞서 이야기한 문제를 피하기 위해 views, url을 별도의 파일로 구성해본다.
# testings/views.py
from django.urls import reverse
from django.views.generic import ListView, DetailView, UpdateView
from .models import Tasting
class TasteListView(ListView):
model = Tasting
class TasteDetailView(DetailView):
model = Tasting
class TasteResultsView(TasteDetailView):
template_name = 'tastings/results.html'
class TasteUpdateView(UpdateView):
model = Tasting
def get_success_url(self):
return reverse('tastings:detail', kwargs={'pk': self.object.pk})
# testings/urls.py
from django.urls import path
from . import views
urlpatterns = [
path(
route='',
view=views.TasteListView.as_view(),
name='list'
),
path(
route='<int:pk>/',
view=views.TasteDetailView.as_view(),
name='detail'
),
path(
route='<int:pk>/results/',
view=views.TasteResultsView.as_view(),
name='results'
),
path(
route='<int:pk>/update/',
view=views.TasteUpdateView.as_view(),
name='update'
)
]
두 개의 파일로 나뉘었고 코드는 늘어났지만 우리가 지향하는 방법이다.
namespace를 tastings로 정의하였다. URL이름은 tasting_detail이 아닌 tastings:detail로 작성하였다.
# 프로젝트 루트의 urls.py
urlpatterns += [
path('tastings/', include('tastings.urls', namespace='tastings')), # 8.3절의 tasting/urls.py로 연결됨
]
# tastings/views.py snippet
class TasteUpdateView(UpdateView):
model = Tasting
def get_success_url(self):
return reverse('tastings:detail', kwargs={'pk': self.object.pk})
{% extends 'base.html' %}
{% block title %}Tastings{% endblock title %}
{% block content %}
<ul>
{% for taste in tastings %}
<li>
<a href="{% url 'tastings:detail' taste.pk %}">{{ taste.title }}</a>
<small>
(<a href="{% url 'tastings:update' taste.pk %}">update</a>)
</small>
</li>
{% endfor %}
</ul>
{% endblock content %}
# urls.py at root of project
urlpatterns += [
path('contact/', include('contactmonger.urls', namespace='contactmonger')),
path('report-problem/', include('contactapp.urls', namespace='contactapp')),
]
{% extends "base.html" %}
{% block title %}Contact{% endblock title %}
{% block content %}
<p>
<a href="{% url 'contactmonger:create' %}">Contact Us</a>
</p> <p>
<a href="{% url 'contactapp:report' %}">Report a Problem</a> </p>
{% endblock content %}
장고의 뷰는 함수로도 볼 수 있다. HTTP 요청 객체를 가져와 HTTP 응답 객체로 바꾼다.
# 함수로서의 장고 함수 기반 뷰
HttpResponse = view(HttpRequest)
# 기본 수학식 형태로 풀이 (remember functions from algebra?)
y = f(x)
# ... 그리고 이를 CBV 예로 변경해 보면 다음과 같다
HttpResponse = View.as_view()(HttpRequest)
기본 형태를 기억하자
from django.http import HttpResponse
from django.views.generic import View
# 함수 기반 뷰의 기본 형태 (FBV)
def simplest_view(request):
# 비즈니스 로직이 여기에 위치한다.
return HttpResponse('FBV')
# 클래스 기반 뷰의 기본 형태 (CBV)
class SimplestView(View):
def get(self, request, *args, **kwargs):
# 비즈니스 로직이 여기에 위치한다.
return HttpResponse('CBV')
# Don't do this!
def ice_cream_store_display(request, store_id):
store = get_object_or_404(Store, id=store_id)
date = timezone.now()
return render(request, 'melted_ice_cream_report.html', locals())
# Don't do this!
def ice_cream_store_display(request, store_id):
store = get_object_or_404(Store, id=store_id)
now = timezone.now()
return render(request, 'melted_ice_cream_report.html', locals())
def ice_cream_store_display(request, store_id):
return render(
request,
'melted_ice_cream_report.html',
{
'store': get_object_or_404(Store, id=store_id),
'now': timezone.now()
}
)