基于类的视图有什么优势?

82

我今天读到Django 1.3 alpha版本已经发布,其中最受瞩目的新功能是引入了基于类的视图
我已经阅读了相关文档,但我发现很难看出使用它们可以获得什么显著优势™,因此我在这里请求一些帮助来理解它们。
让我们从文档中选取一个高级示例

urls.py

from books.views import PublisherBookListView

urlpatterns = patterns('',
    (r'^books/(\w+)/$', PublisherBookListView.as_view()),
)

视图.py

from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from books.models import Book, Publisher

class PublisherBookListView(ListView):

    context_object_name = "book_list"
    template_name = "books/books_by_publisher.html",

    def get_queryset(self):
        self.publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
        return Book.objects.filter(publisher=self.publisher)

    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super(PublisherBookListView, self).get_context_data(**kwargs)
        # Add in the publisher
        context['publisher'] = self.publisher
        return context

现在,让我们将其与我为这个问题而在5分钟内自己创建的“普通视图”解决方案进行比较(如果您发现任何错误,请原谅)。

urls.py

urlpatterns = patterns('books.views',
    url(r'^books/(\w+)/$', 'publisher_books_list', name="publisher_books_list"),
)

视图.py

from django.shortcuts import get_object_or_404
from books.models import Book, Publisher

def publisher_books_list(request, publisher_name):
    publisher = get_object_or_404(Publisher, name__iexact=publisher_name)
    book_list = Book.objects.filter(publisher=publisher)

    return render_to_response('books/books_by_publisher.html', {
        "book_list": book_list,
        "publisher": publisher,
    }, context_instance=RequestContext(request))
第二个版本对我来说看起来:
  • 在功能上等同
  • 更易读 (self.args[0]? 糟糕!)
  • 更短
  • 不违反DRY原则
我是否漏掉了什么重要的东西?为什么我应该使用它们?文档中有吗?如果是这样,那么理想的用例是什么?混合(mixins)真的那么有用吗?
提前感谢任何有贡献的人!
P.S. 对于那些可能会想知道的人,我从来没有被通用视图迷住过:一旦我需要一些高级功能,它们就变得和常规视图一样长了。

4
我也没有看到太大的优势。希望能看到一个详细的回答。 - M. Ryan
1
完全同意。我特别讨厌self.args [0]或self.kwargs ['slug']。现在,为URL参数提供默认值也变得更加困难,例如:def publisher_books_list(request,publisher_name ='Herbert') - Kevin Renskers
5个回答

49

您可以创建一个类的子类,并为特定情况修改像get_context_data这样的方法,而将其余部分保留不变。但是函数不能这样做。

例如,您可能需要创建一个新视图,它执行以前视图的所有操作,但您需要在上下文中包含额外的变量。子类化原始视图并覆盖get_context_data方法。

此外,将呈现模板所需的步骤分解成单独的方法可以促进更清晰的代码 - 方法中要完成的内容越少,理解起来就越容易。使用常规视图函数,所有操作都会被倾泻到一个处理单元中。


2
是的,我能理解这一点。当你试图决定是否应该采用RESTful、拥有完整网站或移动网站时,它可以提供一种易于覆盖和频繁混合的情况。有了这个,可以在功能被派生出来之前尽可能地推迟做出决定。Pylons中的Webware模块就有这个功能,非常有用。话虽如此,通过覆盖__call__()方法,Django早就可以实现基于类的视图。 - Elf Sternberg
9
接受答案,因为它提供了一个非常好的观点...但仍然不感觉需要使用它们,因为我很少有这样的问题需要解决。谢谢! - Agos

17

如果self.args[0]让您不舒服,另一个选择是:

urlpatterns = patterns('books.views',
    url(r'^books/(?P<slug>\w+)/$', 'publisher_books_list', name="publisher_books_list"),
)

那么你可以使用 self.kwargs['slug'],这样会让它稍微更易读一些。


10

你的示例函数和类在功能上并不相等。

基于类的版本免费提供分页功能,并禁止使用GET以外的其他HTTP动词。

如果想要将这个功能添加到你的函数中,代码会变得更加冗长。

但这确实更加复杂。


2
+1 指出了差异,但我个人认为 require_GETdjango-pagination 很容易使用,简洁明了等等,我几乎总是更喜欢它们而不是 cbvs。 - Tomasz Zieliński

4

这是我第一次听到这个 -- 我很喜欢。

我认为这里的优势在于它使得视图与 Django 整体更加一致。模型是类,而我一直觉得视图也应该是类。我知道并非所有东西都是这样,但视图和模型是两种经常使用的类型。

至于技术优势?嗯,在 Python 中,一切都是类(或对象)-- 那真的有什么区别吗?这不是99%的句法糖吗?


我认为一致性可以实现更大程度的代码重用。如果你的视图符合某些模式,它们基本上可以减少很多样板文件。例如,基于模型的表单使用类视图非常快速。如果需要添加几个额外的字段,就会变得有点棘手。如果需要基于三个模型加上两个额外字段的表单,它们可能并不能节省太多的工作量。 - wobbily_col

1

关于基于类的视图,可以将其看作是去掉了Django管理界面的训练轮,因此更加灵活(但也更难理解)。

例如,管理界面中的列表展示明显基于通用的ListView。最简单的列表视图只需要定义一个模型或查询集。

class MyExampleView(ListView);
    model = ExampleModel 

你需要提供自己的模板,但它基本上与最基本的ModelAdmin相同。在模型管理中,list_display属性将告诉它要显示哪些字段,而在ListView中,您可以在模板中执行此操作。
class SpeciesAdmin(admin.ModelAdmin):
    list_display = ['name']
admin.site.register(ExampleModel , ExampleModelAdmin)

使用管理员时,您有一个参数

list_per_page = 100

这定义了每页有多少个对象。列表视图具有

paginate_by = 100

这也能实现同样的效果。同样,如果你想要深度定制管理界面,你会看到很多重叠的地方。

这个网站应该能让您更好地了解它们的功能。

http://ccbv.co.uk/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接