Django, 基于类的视图如何继承基类的属性

2015-05-14 12:33:41 +08:00
 cc7756789
class Links(TemplateView):
template_name = "home/links.html"
def get_context_data(self, **kwargs):
context = super(Links, self).get_context_data(**kwargs)
context['link'] = Friend_Links.objects.all()
context['latest_article_list'] = latest_article_list
context['now'] = time.strftime('%Y-%m-%d %X', time.localtime())
context['ip'] = self.request.META['REMOTE_ADDR']
return context

class Index(Links):
template_name = "home/index.html"
def get_context_data(self, **kwargs):
context = super(Index, self).get_context_data(**kwargs)
return context


class PythonNav(Links):
template_name = "home/python.html"
def get_context_data(self, **kwargs):
context = super(PythonNav, self).get_context_data(**kwargs)
context['python_list'] = Article.objects.filter(category="Python")
return context

class Detail(DetailView):
model = Article
context_object_name = "ar"
template_name = "home/detail.html"
def get_context_data(self, **kwargs):
context = super(Detail, self).get_context_data(**kwargs)
context['latest_article_list'] = latest_article_list
context['now'] = time.strftime('%Y-%m-%d %X', time.localtime())
return context

class ContactMe(Links, CreateView):
model = Contact
template_name = "home/contact.html"
success_url = '/'
def get_context_data(self, **kwargs):
context = super(ContactMe, self).get_context_data(**kwargs)
return context

ContactMe我想继承 Links的 context
为什么会报错
AttributeError at /nav/contact
'ContactMe' object has no attribute 'object'

Detail也想继承Links的
但是也是同样的错误
3879 次点击
所在节点    Python
1 条回复
kellyl
2015-05-19 17:28:43 +08:00
@cc7756789,如果你想得到大家的帮助,首先要把问题内容格式整理好。V2EX支持markdown语法。

建议问题标题修改为「django视图类继承DetailView和TemplateView后报错」,你的问题代码可以格式化如下

<pre>
class Links(TemplateView):
_____template_name = "home/links.html"

_____def get_context_data(self, **kwargs):
__________context = super(Links, self).get_context_data(**kwargs)
__________context['link'] = Friend_Links.objects.all()
__________context['latest_article_list'] = latest_article_list
__________context['now'] = time.strftime('%Y-%m-%d %X', time.localtime())
__________context['ip'] = self.request.META['REMOTE_ADDR']
__________return context

class Detail(DetailView, Links):
_____model = Article
_____context_object_name = "ar"
_____template_name = "home/detail.html"

_____def get_context_data(self, **kwargs):
__________context = super(Detail, self).get_context_data(**kwargs)
__________context['latest_article_list'] = latest_article_list
__________context['now'] = time.strftime('%Y-%m-%d %X', time.localtime())
_____return context
</pre>


这是典型的多重继承方法属性读取顺序问题。在Python里叫"Method Resolution Order",Python的继承多重继承机制请看参考阅读(如果经常写Python面向对象的代码,一定要弄明白多重继承中C3线性继承的使用)。

我们来看一下Detail类的MRO。
>>> Detail.__mro__

....(Detail,
.... django.views.generic.detail.DetailView,
.... django.views.generic.detail.SingleObjectTemplateResponseMixin,
.... gizwits_developer.views.Links,
.... django.views.generic.base.TemplateView,
.... django.views.generic.base.TemplateResponseMixin,
.... django.views.generic.detail.BaseDetailView,
.... django.views.generic.detail.SingleObjectMixin,
.... django.views.generic.base.ContextMixin,
.... django.views.generic.base.View,
.... object)

当页面打开Detail页面时,发生了:

1. django调用视图的self.get方法
2. 根据mro找到django.views.generic.base.TemplateView.get方法,在get方法中调用self.get_context_data(**kwargs)
3. 根据mro,分别调用Detail, DetailView...中的get_context_data,当调用到django.views.generic.detail.SingleObjectMixin.get_context_data时,代码读取`self.object`,但是此时self中没有`object`属性,页面报错。因为`self.obejct`的赋值是在`django.views.generic.detail.BaseDetailView.get`做的,但是get方法被`django.views.generic.base.TemplateView.get`拦截了,导致执行不到。

如何解决(方法很多,这里简单列出两个面向对象的方法实现):

1. 调整__mro__中get的顺序。不建议。因为复杂的mro并不能够简单通过Detail(Links,DetailView)改为Detail(DetailView, Links)能够掉整的;在Detail类复写get也能够解决,但后期代码难维护。
2. 将TemplateView在__mro__中移除。这是我个人建议的方法。其实问题产生的原因是Links和DetailView都是TemplateResponseMixin的子类,交叉继承导致的。DetailView本来就有TemplateView的功能,再次继承的话容易出问题。

解决方法(使用复用的contextMixin类):

<pre>
class LinksContext(ContextMixin):

_____def get_context_data(self, **kwargs):
__________context = super(LinksContext, self).get_context_data(**kwargs)
__________context['link'] = Friend_Links.objects.all()
__________context['latest_article_list'] = latest_article_list
__________context['now'] = time.strftime('%Y-%m-%d %X', time.localtime())
__________context['ip'] = self.request.META['REMOTE_ADDR']
__________return context

class Links(TemplateView, LinksContext):
_____template_name = "home/links.html"

class Detail(DetailView, LinksContext):
_____model = Article
_____context_object_name = "ar"
_____template_name = "home/detail.html"

_____def get_context_data(self, **kwargs):
__________context = super(Detail, self).get_context_data(**kwargs)
__________context['latest_article_list'] = latest_article_list
__________context['now'] = time.strftime('%Y-%m-%d %X', time.localtime())
_____return context
<pre>


参考阅读:
http://python-history.blogspot.com/2010/06/method-resolution-order.html
https://www.python.org/download/releases/2.3/mro

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/191010

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX