SingleObjectMixin
class SingleObjectMixin(ContextMixin):
"""
提供檢索單個對象,並對該對象操做的一些功能
"""
model = None # 模型類 eg:User
queryset = None # 查詢集 eg: User.object.filter(active=True)
# model 和 queryset 指定一個就行 不容許同時指定
# queryset是具備可變值的類屬性,所以在直接使用它時必須當心。在使用它以前,要麼調用它的all()
# 方法,要麼使用它的方法進行檢索 get_queryset(),以處理後臺傳回的拷貝。
slug_field = 'slug' # 模型中包含該字段的名稱
context_object_name = None # 指定在模版的上下文中使用的變量的名稱,全部的字段信息都會被包含
# 在名爲 context_object_name 的對象中,
# 例如 context_object_name = forms
# 假設 forms 相似這樣 {'name': 'monkey'}
# 在模版中 {{ forms.name }} 將會渲染出 name 的 值
slug_url_kwarg = 'slug' # 也是用來檢索惟一的對象,可是它是爲了安全而存在的,默認爲slug
# 用來和pk 一塊兒獲取惟一對象
pk_url_kwarg = 'pk' # 用來檢索惟一的對象的關鍵信息,它默認的是pk 視做模型類的主鍵<id>字段 須要在URL中傳入
query_pk_and_slug = False # 若是爲 True 則肯定惟一的對象時 會同時使用pk 和 字段 來肯定 默認是False
def get_object(self, queryset=None):
"""
返回視圖要顯示的對象的信息
默認狀況下會從URL中獲取pk或slug 參數來肯定惟一的對象
並將這個對象返回 只要返回的是一個具體的對象就能夠 不管是誰的對象
並不會被 model或query_set屬性約束,在子類中能夠覆蓋這個方法返
回任何的對象均可以
"""
if queryset is None:
# 若是沒有定義 query_set 屬性 執行 get_queryset 方法 該方法使用 model 屬性返回
# 一個指定 model 全部實例的查詢集 若是 get_queryset 方法沒有在子類中被重寫
queryset = self.get_queryset()
pk = self.kwargs.get(self.pk_url_kwarg) # 獲取主鍵id值
slug = self.kwargs.get(self.slug_url_kwarg) # 獲取slug 值
# 若是pk 不爲空,經過pk獲取查詢集 保存在 queryset中
# 若是slug 不爲空且 pk 也不爲空 使用slug 過濾queryset的結果保存在queryset中
# 若是都爲空 爆拋出錯誤 沒法找到惟一的對象
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."
% self.__class__.__name__)
try:
# 從過濾後的查詢集中獲取惟一的對象 成功則返回這個對象 失敗 報 404 錯誤 頁面不存在
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):
"""
經過 model 或 queryset 屬性肯定查詢集 成功返回查詢集 失敗主動拋出錯誤
"""
if self.queryset is None:
if self.model: # 若是 queryset 爲None 且 model 屬性存在 返回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() # 若是 queryset 被子類重寫了 則直接返回.all() 全部的對象集合
def get_slug_field(self):
"""
獲取將由slug用於查找的slug字段的名稱。
"""
return self.slug_field
def get_context_object_name(self, obj):
"""
獲取在上下文模版中使用的 用於對象的名稱。
用戶指定了context_object_name 屬性 則使用其值
沒有則使用 model 的名字 所有小寫
源碼< self.model_name = self.object_name.lower() >
"""
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):
"""
將單個對象 插入上下文字典中,以便於在模版中使用.
子類若是覆蓋此方法,必定要返回上下文字典 不然將沒法在Template中組織上下文
也就是無法渲染模版了
"""
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
# 將原有的kwargs 傳入字典中
context.update(kwargs)
return super(SingleObjectMixin, self).get_context_data(**context)
BaseDetailView
class BaseDetailView(SingleObjectMixin, View):
"""
用於顯示單個對象的基本視圖
由於繼承View 所以 它必須實現View 約束的方法中的某個 通常來講是 get
"""
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object) # 覆蓋object <SignalObjectMixin 中 context.update(kwargs)>
# 這樣 context 原有的object 被更新爲 傳入的 self.object 事實上他們是一致的
return self.render_to_response(context) # 將模版和上下文字典渲染成響應對象 並返回
SingleObjectTemplateResponseMixin
class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
"""
絕大多數的功能都在父類中實現的,參看父類的源碼解析
該類的做用我認爲有一下幾點
1 解耦合 將模版和上下文字典 結合生成響應對象的方法繼承自父類的 render_to_response() 而該方法調用
了肯定 模版名稱的方法 用來肯定使用的 模版列表。該類可不依賴 父類 來獲取模版
2 容許不顯示的給 template_name 值 來本身推斷模版
# 這樣的設計 但願約束使用者 編寫通用風格的模版名而減小代碼量 提高代碼的可讀性和可維護行
# 可是 每每不利於讓使用者知道他在幹嗎~
"""
template_name_field = None # 默認的參數
template_name_suffix = '_detail' # django 主動的推斷模版名時須要的後綴
def get_template_names(self):
"""
重寫了 父類的方法
做用 推斷模版名、解耦
返回用於請求的模板名稱列表。 若是render_to_response被覆蓋,則可能不會被調用。 返回如下列表:
*視圖上``template_name''的值(若是提供)
*模板上的template_name_field字段的內容
視圖正在操做的對象實例(若是有)
*``<app_label> / <model_name> <template_name_suffix> .html``
"""
try:
# 嘗試獲取 模版的 文件名列表 get_template_names() 被父類的render_to_response方法調用
names = super(SingleObjectTemplateResponseMixin, self).get_template_names()
except ImproperlyConfigured:
# 若是沒有指定 template_name 就實現本身的獲取 方法 以解耦對父類的依賴
# 初始化一個 列表
names = []
# 若是設置了self.template_name_field,則獲取該字段的值 用做模版的名字
if self.object and self.template_name_field:
name = getattr(self.object, self.template_name_field, None)
if name:
names.insert(0, name)
# 最不明確的選項是默認的 < app >/< model >_detail.html;
# _detail 是 template_name_suffix 的值
# 僅在有關對象是模型時才使用此功能。
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 hasattr(self, 'model') and self.model is not None and issubclass(self.model, models.Model):
# 不指定 模版時 django 試圖拼接出一個模版名,我不以爲這是一個很好的設計
# 雖然它使得框架更爲的聰明,最重要的是 但願使用者使用 統一風格的模版名稱
# 可是這不可避免的加劇了 負擔 同時 使用者 可能會不清楚他們作了什麼
names.append("%s/%s%s.html" % (
self.model._meta.app_label,
self.model._meta.model_name,
self.template_name_suffix
))
# 若是 咱們最終仍是沒有獲得指望的 一個可用的模版名稱的話 就只能拋出異常
if not names:
raise
return names
DetailView
class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView):
"""
渲染對象的「詳細」視圖。默認狀況下,這是一個從 self.queryset 中查找的模型實例,可是
視圖將經過覆蓋 self.get_object() 來渲染任意的對象
方法的流程
dispatch() 請求分發
http_method_not_allowed() 方法過濾
get_template_names() 獲取模版名
get_slug_field() 獲取用於肯定對象的字段
get_queryset() 獲取查詢集
get_object() 使用 pk slug 等獲取惟一的對象
get_context_object_name() 獲取模版中使用的 上下文字典的名稱
get_context_data() # 獲取上下文字典數據
render_to_response() 返回響應體
"""
# 全部的事情都在父類中完成 儘量的理解 MRO 以及每個類 實現的方法,深入的體會Mixin 拆分的精髓 我以爲這是django 中的精華。