
detail view 는 원하는 데이터 한 가지에 대한 결과를 조회하는 역할을 수행하는 view 입니다.
하나의 데이터를 반환하는 역할을 수행하기 때문에 웹 사이트의 마이페이지나 제품 상세 페이지 등 단일 객체에 대한 view 로 사용하기 용이합니다.
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
slug는 사람이 읽을 수 있고 URL에 적합한 문자열 식별자이며, 일반적으로 데이터의 제목(title) 등을 기반으로 생성되어 웹 주소(URL)의 일부로 사용됩니다.
Return the object the view is displaying.
Requireself.querysetand apkorslugargument in the URLconf.
Subclasses can override this to return any object.
def get_object(self, queryset=None):
# 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
지정된 pk 나 slug 를 검색어로 데이터베이스에서 해당하는 데이터를 조회하고, 반환합니다.
만약 pk 나 slug 중 하나도 없다면 에러를 발생시킵니다. 조회된 데이터가 존재하지 않다면 http 404 에러를 발생합니다.
Return the
QuerySetthat 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.
def get_queryset(self):
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()
인스턴스에 저장된 쿼리셋이 없다면 설정된 model의 에 대해 쿼리셋을 생성합니다.
만약 model 이 설정되지 않았다면 에러가 발생합니다.
쿼리셋은 조회된 데이터가 아닌 쿼리를 담고 있는 객체입니다.
def get_slug_field(self):
"""Get the name of a slug field to be used to look up by slug."""
return self.slug_field
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
context_object_name 을 반환합니다.
설정되지 않았다면 자동으로 model 이름을 소문자로 변환하여 사용합니다.
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)
get_object 메소드로 초기화된 self.object 를 context 로 반환합니다.
SingleObjectMixin 는 ContextMixin 를 상속했기 때문에 get_context_data 메소드를 사용할 수 있습니다.
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 의 기능을 view 의 형태로 사용할 수 있도록 View 를 함께 상속한 클래스입니다.
http method 중 get 에 대하여 SingleObjectMixin 의 기능을 제공합니다.
BaseDetailView 가 상속한 두 클래스에는 render_to_response 가 선언되지 않았습니다.
따라서 문법적으로 에러가 발생합니다.
다만, 이 클래스는 다른 믹스인과 함께 조합하여 사용되는 전제를 가지고 있기 때문에, 해당 믹스인의 render_to_response 를 사용할 수 있습니다.
Base 라는 이름처럼 다른 view 의 부모 클래스가 되는 추상 클래스이다.
Return a list of template names to be used for the request. May not be called if render_to_response() is overridden.
Return the following list:
- the value of
template_nameon the view (if provided)- the contents of the
template_name_fieldfield on the object instance that the view is operating upon (if available)<app_label>/<model_name><template_name_suffix>.html
class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
template_name_field = None
template_name_suffix = "_detail"
def get_template_names(self):
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 self.template_name_field is set, grab the value of the field
# of that name from the object; this is the most specific template
# name, if given.
if self.object and self.template_name_field:
name = getattr(self.object, self.template_name_field, None)
if name:
names.insert(0, name)
# The least-specific option is the default <app>/<model>_detail.html;
# only use this if the object in question is a model.
if isinstance(self.object, models.Model):
object_meta = self.object._meta
names.append(
"%s/%s%s.html"
% (
object_meta.app_label,
object_meta.model_name,
self.template_name_suffix,
)
)
elif getattr(self, "model", None) is not None and issubclass(
self.model, models.Model
):
names.append(
"%s/%s%s.html"
% (
self.model._meta.app_label,
self.model._meta.model_name,
self.template_name_suffix,
)
)
# If we still haven't managed to find any template names, we should
# re-raise the ImproperlyConfigured to alert the user.
if not names:
raise
return names
TemplateResponseMixin 에 선언된 get_template_names 메소드를 통해 미리 설정된 template name 을 확인합니다.
만약 미리 설정된 template name 이 없다면, 다음 조건에 따라 template name 을 자동으로 설정합니다.
self.object 는 get_object() 메소드에 의해 설정되는 인스턴스입니다.
self.object 가 존재하고, self.template_name_field 가 설정되어 있는 경우이 경우는 object 가 template_name_field 속성을 가지고 있는지만 확인합니다.
object 에 template_name_field 에 해당하는 속성이 존재한다면, names 의 첫 번째 인덱스에 self.template_name_field 룰 추가합니다.
self.object 의 meta 정보 중에 앱 이름과 모델 이름, template_name_suffix 를 names 에 추가합니다.
Model 클래스의 자식 클래스인 경우view 에서 model 변수를 설정한 경우에 실행되는 조건입니다. object 가 설정되지 않은 경우에 안전장치와 같은 역할을 수행하며, 설정된 model 의 meta 정보를 names 에 추가합니다.
template name 을 결정하는 경우는 다음으로 정리됩니다.
self.object 가 존재하는지self.object 에 template_name_field 가 있는지self.object 가 Model 클래스인지self.object 가 없지만, 클래스에 model 속성이 설정되었는지아무것도 설정되지 않았다면 get_template_names() 는 에러를 반환합니다.
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()`.
"""
앞서 정리했던 mixin 과 view 를 상속하여 하나의 객체에 대한 정보와 template 을 제공하는 detail view 로 완성합니다.
DetailView
├── SingleObjectTemplateResponseMixin
│ └── TemplateResponseMixin
│
├── BaseDetailView
├── SingleObjectMixin
│ └── ContextMixin
└── View
DetailView 가 상속하는 mixin 과 view 를 정리한 계층도입니다.
2가지의 클래스를 상속하는 구조에서 Django 의 MVT 패턴이 드러납니다.
view 에서 model 에 접근하고, template 을 제어하는 모습으로 보입니다.
DetailView 가 상속하는 클래스들의 클래스 변수들을 알고 있어야 합니다.
| 클래스 | 변수 | 설명 |
|---|---|---|
SingleObjectMixin | model | 조회할 모델 클래스 지정 |
SingleObjectMixin | queryset | 조회할 QuerySet 지정 (선택적으로 model 대신 사용 가능) |
| 클래스 | 변수 | 기본값 | 설명 |
|---|---|---|---|
TemplateResponseMixin | template_name | None | 사용할 템플릿 경로를 직접 지정 |
SingleObjectTemplateResponseMixin | template_name_field | None | 객체의 특정 필드에서 템플릿 경로를 가져오도록 지정 |
SingleObjectTemplateResponseMixin | template_name_suffix | "_detail" | 템플릿 자동 유추 시 뒤에 붙는 접미사 |
SingleObjectMixin | slug_field | "slug" | 슬러그 조회 시 사용할 모델 필드명 |
SingleObjectMixin | slug_url_kwarg | "slug" | URLConf에서 slug를 꺼낼 키 이름 |
SingleObjectMixin | pk_url_kwarg | "pk" | URLConf에서 pk를 꺼낼 키 이름 |
SingleObjectMixin | query_pk_and_slug | False | pk와 slug를 동시에 사용해 조회할지 여부 |
SingleObjectMixin | context_object_name | None | 템플릿에서 사용할 객체 이름 지정 |
ContextMixin | extra_context | None | 템플릿에 추가로 전달할 context 딕셔너리 |