@
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.htmlhttps://www.python.org/download/releases/2.3/mro