[Django] Base views / Generic display views / Generic date views
🎈Base Views
View
-
최상위 부모 제네릭 뷰 클래스
-
직접 쓸 일은 거의 없지만 다른 클래스들을 사용함으로서 간접적으로 항상 사용되고 있음
-
http method별로 지정 이름의 멤버 함수를 호출하도록 구현
-
__init__
: as_view에 인자를 넘겨주게 되면 인자가 **kwargs로 넘어와 dict를 순회하며 해당 설정을 해주게 됨
def __init__(self, **kwargs):
"""
Constructor. Called in the URLconf; can contain helpful extra
keyword arguments, and other things.
"""
# Go through keyword arguments, and either save their values to our
# instance, or raise an error.
for key, value in kwargs.items():
setattr(self, key, value)
-
as_view
: view를 하나 만들어서 return -
dispatch
: as_view 메소드 내부에서 호출
request method를 확인하고 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] 안에 있는 정상적인 method라면 request에서 이를 가져옴
정상적인 method가 아니라면 http_method_not_allowed 호출
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
TemplateView
class TemplateView(TemplateResponseMixin, ContextMixin, View):
"""
Render a template. Pass keyword arguments from the URLconf to the context.
"""
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
TemplateResponseMixin
template의 기본적인 설정들, template 이름, rendering하는 과정 등 담당
class TemplateResponseMixin:
"""A mixin that can be used to render a template."""
template_name = None
template_engine = None
response_class = TemplateResponse
content_type = None
def render_to_response(self, context, **response_kwargs):
"""
Return a response, using the `response_class` for this view, with a
template rendered with the given context.
Pass response_kwargs to the constructor of the response class.
"""
response_kwargs.setdefault('content_type', self.content_type)
return self.response_class(
request=self.request,
template=self.get_template_names(),
context=context,
using=self.template_engine,
**response_kwargs
)
def get_template_names(self):
"""
Return a list of template names to be used for the request. Must return
a list. May not be called if render_to_response() is overridden.
"""
if self.template_name is None:
raise ImproperlyConfigured(
"TemplateResponseMixin requires either a definition of "
"'template_name' or an implementation of 'get_template_names()'")
else:
return [self.template_name]
ContextMixin
정적인 context에 대해서는extra_context
변수 사용, 동적으로 사용해야하는 경우get_context_data
사용
class ContextMixin:
"""
A default context mixin that passes the keyword arguments received by
get_context_data() as the template context.
"""
extra_context = None
def get_context_data(self, **kwargs):
kwargs.setdefault('view', self)
if self.extra_context is not None:
kwargs.update(self.extra_context)
return kwargs
예시
#방법 1
urlpatterns = [
path('', TemplateView.as_view(template_name='root.html'), name='root'),
]
#방법 2
class RootView(TemplateView):
template_name = 'root.html'
urlpatterns = [
path('', RootView.as_view(), name='root'),
]
RedirectView
class RedirectView(View):
"""Provide a redirect on any GET request."""
permanent = False
url = None
pattern_name = None
query_string = False
def get_redirect_url(self, *args, **kwargs):
"""
Return the URL redirect to. Keyword arguments from the URL pattern
match generating the redirect request are provided as kwargs to this
method.
"""
if self.url:
url = self.url % kwargs
elif self.pattern_name:
url = reverse(self.pattern_name, args=args, kwargs=kwargs)
else:
return None
args = self.request.META.get('QUERY_STRING', '')
if args and self.query_string:
url = "%s?%s" % (url, args)
return url
def get(self, request, *args, **kwargs):
url = self.get_redirect_url(*args, **kwargs)
if url:
if self.permanent:
return HttpResponsePermanentRedirect(url)
else:
return HttpResponseRedirect(url)
else:
logger.warning(
'Gone: %s', request.path,
extra={'status_code': 410, 'request': request}
)
return HttpResponseGone()
def head(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def options(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
-
parmanet
- True: 301 응답(영구적 이동)
- False: 302 응답(임시 이동) - default
-
url
-
pattern_name
: URL Reverse를 수행할 문자열 -
query_string
: QueryString을 그대로 넘길 것인지? -
get
: get_redirect_url을 호출하여 url을 얻음
url을 얻지 못하면 error 출력, 제대로 얻었다면 parmanent에 의해 분기 처리 -
get_redirect_url
: 클래스 변수 url을 통해 직접 url을 받아오거나 pattern_name을 통해 url의 name 값을 받아왔으면 이를 return
예시
- myproject/urls.py
urlpatterns = [
path('', RedirectView.as_view(pattern_name='instagram:post_list', name='root'),
]
- instagram/urls.py
app_name = 'instagram'
urlpatterns = [
path('', views.post_list, name='post_list'),
]
🎈 Generic display views
DetailView
코드 보기
1개 모델의 1개 Object에 대한 템플릿 처리
각 상세페이지 보여주는 역할
class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView):
"""
Render a "detail" view of an object.
By default this is a model instance looked up from `self.queryset`, but the
view will support display of *any* object by overriding `self.get_object()`.
"""
-
SingleObjectTemplateResponseMixin
template_name 값을 설정하면 이에 맞는 templates 반환
따로 설정해주지 않으면앱이름/모델이름_detail.html
-
BaseDetailView
get 요청에 대해 db로부터 하나의 object를 가져와 이를 context에 담아 rendering
class BaseDetailView(SingleObjectMixin, View):
"""A base view for displaying a single object."""
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
-
SingleObjectMixin
-
pk_url_kwarg
,slug_url_kwargs
: 기본값으로 pk, slug로 지정되어 있음 (urls에서 넘겨주는 인자 이름이 다르다면 해당 값 변경해주기) -
get_object
: 모델로부터 object 얻어옴 -
get_queryset
: 기본적으로 모델의 모든 객체 반환, 위에 queryset 지정되어있으면 queryset에 맞게 객체 return -
get_context_data
: 모델의 이름으로 context를 넘겨주는 역할
-
class SingleObjectMixin(ContextMixin):
"""
Provide the ability to retrieve a single object for further manipulation.
"""
model = None
queryset = None
slug_field = 'slug'
context_object_name = None
slug_url_kwarg = 'slug'
pk_url_kwarg = 'pk'
query_pk_and_slug = False
def get_object(self, queryset=None):
"""
Return the object the view is displaying.
Require `self.queryset` and a `pk` or `slug` argument in the URLconf.
Subclasses can override this to return any object.
"""
# Use a custom queryset if provided; this is required for subclasses
# like DateDetailView
if queryset is None:
queryset = self.get_queryset()
# Next, try looking up by primary key.
pk = self.kwargs.get(self.pk_url_kwarg)
slug = self.kwargs.get(self.slug_url_kwarg)
if pk is not None:
queryset = queryset.filter(pk=pk)
# Next, try looking up by slug.
if slug is not None and (pk is None or self.query_pk_and_slug):
slug_field = self.get_slug_field()
queryset = queryset.filter(**{slug_field: slug})
# If none of those are defined, it's an error.
if pk is None and slug is None:
raise AttributeError(
"Generic detail view %s must be called with either an object "
"pk or a slug in the URLconf." % self.__class__.__name__
)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
return obj
def get_queryset(self):
"""
Return the `QuerySet` that will be used to look up the object.
This method is called by the default implementation of get_object() and
may not be called if get_object() is overridden.
"""
if self.queryset is None:
if self.model:
return self.model._default_manager.all()
else:
raise ImproperlyConfigured(
"%(cls)s is missing a QuerySet. Define "
"%(cls)s.model, %(cls)s.queryset, or override "
"%(cls)s.get_queryset()." % {
'cls': self.__class__.__name__
}
)
return self.queryset.all()
def get_slug_field(self):
"""Get the name of a slug field to be used to look up by slug."""
return self.slug_field
def get_context_object_name(self, obj):
"""Get the name to use for the object."""
if self.context_object_name:
return self.context_object_name
elif isinstance(obj, models.Model):
return obj._meta.model_name
else:
return None
def get_context_data(self, **kwargs):
"""Insert the single object into the context dict."""
context = {}
if self.object:
context['object'] = self.object
context_object_name = self.get_context_object_name(self.object)
if context_object_name:
context[context_object_name] = self.object
context.update(kwargs)
return super().get_context_data(**context)
예시
- instagram/views.py
class PostDetailView(DetailView):
model = Post
def get_queryset(self):
qs = super().get_queryset()
if not self.request.user.is_authoricated:
qs = qs.filter(is_public=True)
return qs
post_detail = PostDetailView.as_view()
ListView
코드 보기
1개 모델에 대한 List 템플릿 처리
class ListView(MultipleObjectTemplateResponseMixin, BaseListView):
"""
Render some list of objects, set by `self.model` or `self.queryset`.
`self.queryset` can actually be any iterable of items, not just a queryset.
"""
MultipleObjectTemplateResponseMixin
내부적으로 pagination이 구현되어있음
class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
"""Mixin for responding with a template and list of objects."""
template_name_suffix = '_list'
def get_template_names(self):
"""
Return a list of template names to be used for the request. Must return
a list. May not be called if render_to_response is overridden.
"""
try:
names = super().get_template_names()
except ImproperlyConfigured:
# If template_name isn't specified, it's not a problem --
# we just start with an empty list.
names = []
# If the list is a queryset, we'll invent a template name based on the
# app and model name. This name gets put at the end of the template
# name list so that user-supplied names override the automatically-
# generated ones.
if hasattr(self.object_list, 'model'):
opts = self.object_list.model._meta
names.append("%s/%s%s.html" % (opts.app_label, opts.model_name, self.template_name_suffix))
elif not names:
raise ImproperlyConfigured(
"%(cls)s requires either a 'template_name' attribute "
"or a get_queryset() method that returns a QuerySet." % {
'cls': self.__class__.__name__,
}
)
return names
BaseListView
class BaseListView(MultipleObjectMixin, View):
"""A base view for displaying a list of objects."""
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty:
# When pagination is enabled and object_list is a queryset,
# it's better to do a cheap query than to load the unpaginated
# queryset in memory.
if self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists'):
is_empty = not self.object_list.exists()
else:
is_empty = not self.object_list
if is_empty:
raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.") % {
'class_name': self.__class__.__name__,
})
context = self.get_context_data()
return self.render_to_response(context)
예시
django-bootstrap-v5를 사용하여 pagination
- instagram/views.py
post_list = ListView.as_view(model=Post, paginate_by=2)
- templates/instagram/post_list.html
{% if is_paginated%}
{% bootstrap_pagination page_obj size="small" justify_content="center"%}
{% endif %}
🎈Generic date views
ArchiveIndexView
날짜별로 최신 개체를 표시하는 최상위 인덱스 페이지
옵션
model
date_field
: 정렬 기준 필드date_list_period
(디폴트: year)
context
latest
: Querysetdate_list
: 등록된 Record의 년도 목록
디폴트 template_name_suffix: _archive
예시
- views.py
from django.views.generic import ArchiveIndexView
post_archive = ArchiveIndexView.as_view(model=Post, date_field='post_detail')
- urls.py
urlpatterns = [
path('archive/', views.post_archive, name='post_archive'),
]
- post_archive.html
{{latest}}
{% for date in date_list %}
{{date.year}}
{% endfor %}
YearArchiveView
주어진 연도의 목록
옵션
model
date_field
date_list_period
(디폴트: mohth)make_object_list
context
year
previos_year
next_year
date_list
object_list
디폴트 template_name_suffix: _archive_year
MonthArchiveView
옵션
month_format
context
month
previous_month
next_month
date_list
object_list
디폴트 template_name_suffix: archive_month
참고
https://docs.djangoproject.com/en/3.2/ref/class-based-views/generic-date-based/#django.views.generic.dates.BaseArchiveIndexView
https://ssungkang.tistory.com/entry/Django-CBV-1-CBV-%EC%99%80-Base-Views?category=320582
Author And Source
이 문제에 관하여([Django] Base views / Generic display views / Generic date views), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@chldppwls12/django-BaseViews-GenericDisplayViews저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)