Django中的类视图

62 投票
9 回答
15428 浏览
提问于 2025-04-11 00:14

Django的视图指向一个函数,这样做有时候会让人觉得麻烦,特别是当你只想稍微改动一下功能的时候。虽然我可以在这个函数里加很多参数,甚至更多的条件判断,但我更想用面向对象的方法来解决这个问题。

举个例子,我有一个页面用来显示用户的信息。这个页面和显示一个组的页面非常相似,但它们又不完全一样,所以不能简单地用另一个数据模型来处理。组里面还有成员等等...

一种方法是把视图指向类的方法,然后扩展这个类。有没有人尝试过这种方法,或者有其他的想法呢?

9 个回答

9

如果你只是想展示模型中的数据,为什么不使用Django通用视图呢?这些通用视图的设计就是为了让你轻松地展示模型中的数据,而不需要自己写视图,也不用操心如何把网址参数映射到视图、获取数据、处理特殊情况、渲染输出等等。

13

我需要使用基于类的视图,但我希望在我的URL配置中能够直接使用类的全名,而不必每次都先实例化这个视图类。让我找到解决办法的是一个出乎意料的简单元类:

class CallableViewClass(type):
    def __call__(cls, *args, **kwargs):
        if args and isinstance(args[0], HttpRequest):
            instance = super(CallableViewClass, cls).__call__()
            return instance.__call__(*args, **kwargs)
        else:
            instance = super(CallableViewClass, cls).__call__(*args, **kwargs)
            return instance


class View(object):
    __metaclass__ = CallableViewClass

    def __call__(self, request, *args, **kwargs):
        if hasattr(self, request.method):
            handler = getattr(self, request.method)
            if hasattr(handler, '__call__'):
                return handler(request, *args, **kwargs)
        return HttpResponseBadRequest('Method Not Allowed', status=405)

现在我可以既实例化视图类,也可以将这些实例当作视图函数使用,或者我可以直接在我的URL配置中指向我的类,让元类为我实例化(并调用)视图类。这是通过检查__call__的第一个参数来实现的——如果这个参数是HttpRequest,那么它一定是一个实际的HTTP请求,因为用HttpRequest实例去实例化视图类是没有意义的。

class MyView(View):
    def __init__(self, arg=None):
        self.arg = arg
    def GET(request):
        return HttpResponse(self.arg or 'no args provided')

@login_required
class MyOtherView(View):
    def POST(request):
        pass

# And all the following work as expected.
urlpatterns = patterns(''
    url(r'^myview1$', 'myapp.views.MyView', name='myview1'),
    url(r'^myview2$', myapp.views.MyView, name='myview2'),
    url(r'^myview3$', myapp.views.MyView('foobar'), name='myview3'),
    url(r'^myotherview$', 'myapp.views.MyOtherView', name='otherview'),
)

(我在这里发布了一个代码片段:http://djangosnippets.org/snippets/2041/

46

我自己创建并使用了通用视图类,定义了__call__,这样这个类的实例就可以像函数一样被调用。我非常喜欢这样做;虽然Django的通用视图通过关键字参数允许一些自定义,但面向对象的通用视图(如果它们的行为分成多个独立的方法)可以通过子类化实现更细致的自定义,这让我能减少重复代码的情况。(每次需要调整Django的通用视图不太允许的东西时,我都厌倦了重写相同的创建/更新视图逻辑)。

我在djangosnippets.org上发布了一些代码。

我看到的唯一真正的缺点是内部方法调用的增多,这可能会对性能产生一些影响。不过我认为这并不是太大的问题;在网页应用中,Python代码执行通常不会成为性能瓶颈。

更新:Django自己的通用视图现在是基于类的。

更新:顺便说一下,自从写下这个回答后,我对基于类的视图的看法发生了变化。在几个项目中广泛使用后,我觉得它们虽然写起来很简洁(符合DRY原则),但后期阅读和维护起来非常困难,因为功能分散在很多不同的地方,而且子类对父类和混合类的每个实现细节都依赖。我现在觉得TemplateResponse和视图装饰器是分解视图代码的更好方法。

撰写回答