V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
cc7756789
V2EX  ›  Python

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

  •  
  •   cc7756789 · 2015-05-14 12:33:41 +08:00 · 3874 次点击
    这是一个创建于 3508 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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的
    但是也是同样的错误
    1 条回复    2015-05-19 17:28:43 +08:00
    kellyl
        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
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5294 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 07:17 · PVG 15:17 · LAX 23:17 · JFK 02:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.