์ต์์ ๋ถ๋ชจ ์ ๋ค๋ฆญ ๋ทฐ ํด๋์ค
์ง์ ์ธ ์ผ์ ๊ฑฐ์ ์์ง๋ง ๋ค๋ฅธ ํด๋์ค๋ค์ ์ฌ์ฉํจ์ผ๋ก์ ๊ฐ์ ์ ์ผ๋ก ํญ์ ์ฌ์ฉ๋๊ณ ์์
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)
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
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
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'),
]
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
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
์์
urlpatterns = [
path('', RedirectView.as_view(pattern_name='instagram:post_list', name='root'),
]
app_name = 'instagram'
urlpatterns = [
path('', views.post_list, name='post_list'),
]
์ฝ๋ ๋ณด๊ธฐ
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)
์์
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()
์ฝ๋ ๋ณด๊ธฐ
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
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
post_list = ListView.as_view(model=Post, paginate_by=2)
{% if is_paginated%}
{% bootstrap_pagination page_obj size="small" justify_content="center"%}
{% endif %}
๋ ์ง๋ณ๋ก ์ต์ ๊ฐ์ฒด๋ฅผ ํ์ํ๋ ์ต์์ ์ธ๋ฑ์ค ํ์ด์ง
์ต์
model
date_field
: ์ ๋ ฌ ๊ธฐ์ค ํ๋date_list_period
(๋ํดํธ: year)context
latest
: Querysetdate_list
: ๋ฑ๋ก๋ Record์ ๋
๋ ๋ชฉ๋ก๋ํดํธ template_name_suffix: _archive
์์
from django.views.generic import ArchiveIndexView
post_archive = ArchiveIndexView.as_view(model=Post, date_field='post_detail')
urlpatterns = [
path('archive/', views.post_archive, name='post_archive'),
]
{{latest}}
{% for date in date_list %}
{{date.year}}
{% endfor %}
์ฃผ์ด์ง ์ฐ๋์ ๋ชฉ๋ก
์ต์
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
์ต์
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