说是完结, 马上又开始写进阶篇了.
本章不会为博客项目增加新功能, 但是也同样重要, 因为我们要学习高逼格的基于类的视图.
什么是类视图
前面章节中写的所有视图都是基于函数的, 即 def; 而类视图是基于类的, 即 class.
有编程基础的同学都知道, 类是面向对象技术中非常重要的概念. 具有复杂数据, 功能的类, 可以通过继承轻而易举的将自身特性传递给另一个类, 从而实现代码的高效复用.
相比以前的函数视图, 类视图有以下优势:
HTTP 方法 (GET,POST 等) 相关的代码, 可以通过方法而不是条件分支来组织
可以通过诸如 mixins(多重继承)之类的面向对象技术将代码分解为可重用组件
说的都是什么意思? 通过例子来感受一下.
列表
函数和类
假设我们有一个博客列表 https://www.dusaiphoto.com/article/detail/16/ , 列表既有 GET 方法, 又有 POST 方法, 那么用视图函数看起来像这样:
views.py
- def article_list_example(request):
- """处理 GET 请求"""
- if request.method == 'GET':
- articles = ArticlePost.objects.all()
- context = {'articles': articles}
- return render(request, 'article/list.html', context)
而在类视图中, 则变为这样:
views.py
- from django.views import View
- class ArticleListView(View):
- """处理 GET 请求"""
- def get(self, request):
- articles = ArticlePost.objects.all()
- context = {'articles': articles}
- return render(request, 'article/list.html', context)
从本质上讲, 基于类的视图允许你使用不同的类实例方法 (即上面的 def get()) 响应不同的 HTTP 请求方法, 而不需要使用条件分支代码. 这样做的好处是把不同的 HTTP 请求都分离到独立的函数中, 逻辑更加清晰, 并且方便复用.
需要注意的是, 因为 Django 的 URL 解析器希望将请求发送到函数而不是类, 所以类视图有一个 as_view()方法, 该方法返回一个函数, 当请求匹配关联模式的 URL 时, 则调用该函数.
即, 视图函数的 url 原本写为:
urls.py
- ...
- urlpatterns = [
- path('...', views.article_list_example, name='...'),
- ]
类视图的 url 需改写为:
urls.py
- ...
- urlpatterns = [
- path('...', views.ArticleListView.as_view(), name='...'),
- ]
通用视图
像列表这样的功能在 web 开发中是很常见的, 开发者会一遍又一遍写几乎相同的列表逻辑. Django 的通用视图正是为缓解这种痛苦而开发的. 它们对常用模式进行抽象, 以便你快速编写公共视图, 而无需编写太多代码.
因此用列表通用视图改写如下:
views.py
- from django.views.generic import ListView
- class ArticleListView(ListView):
- # 上下文的名称
- context_object_name = 'articles'
- # 查询集
- queryset = ArticlePost.objects.all()
- # 模板位置
- template_name = 'article/list.html'
列表继承了父类 ListView, 也就获得了父类中的处理列表的方法, 因此你可以看到, 我们在自己的类中没有写任何处理的逻辑, 仅仅是赋值了几个变量而已.
动态过滤
从数据库中筛选特定的内容也是常见的需求, 类视图如何实现呢?
你可能想到了, 将上面代码中改为 queryset = ArticlePost.objects.filter()就可以了.
除此之外, 更好的办法是覆写 get_queryset()方法:
views.py
- ...
- class ArticleListView(ListView):
- context_object_name = 'articles'
- template_name = 'article/list.html'
- def get_queryset(self):
- """
- 查询集
- """ queryset = ArticlePost.objects.filter(title='Python')
- return queryset
例子中只是过滤出标题为 "Python" 的文章而已, 有些大材小用了; 但是你可以在 get_queryset()中写复杂的联合查询逻辑, 满足个性化的功能.
添加上下文
在博客列表的设计时, 我们返回给模板的上下文除了 articles 以外, 还有很多额外的信息, 如 order,search; 在类视图中同样可以实现, 改写 get_context_data()方法即可:
views.py
- ...
- class ArticleListView(ListView):
- ...
- def get_context_data(self, **kwargs):
- # 获取原有的上下文
- context = super().get_context_data(**kwargs)
- # 增加新上下文
- context['order'] = 'total_views'
- return context
除此之外, ListView 还有些别的方法可以覆写, 深入了解可以看这里: 官方文档
混入类
混入类 (Mixin) 是指具有某些功能, 通常不独立使用, 提供给其他类继承功能的类. 嗯, 就是 "混入" 的字面意思.
前面的列表视图中已经有 get_context_data()方法了. 假设需要写一个功能类似的视频列表, 就可以用 Mixin 来避免重复代码:
views.py
- ...
- class ContextMixin:
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['order'] = 'total_views'
- return context
- class ArticleListView(ContextMixin, ListView):
- ...
- class VideoListView(ContextMixin, ListView):
- ...
通过混入, 两个子类都获得了 get_context_data()方法.
从语法上看, 混入是通过多重继承实现的. 有区别的是, Mixin 是作为功能添加到子类中的, 而不是作为父类.
实际上 Django 内置了很多通用的 Mixin 类, 实现了大部分常用的功能, 点这里深入了解: 官方文档
详情页
既然列表都有通用视图, 详情页当然也有对应的 DetailView.
用类视图写一个简单的详情页 https://www.dusaiphoto.com/article/detail/19/ :
views.py
- from django.views.generic import DetailView
- class ArticleDetailView(DetailView):
- queryset = ArticlePost.objects.all()
- context_object_name = 'article'
- template_name = 'article/detail.html'
然后配置 url:
urls.py
- ...
- urlpatterns = [
- # 详情类视图
- path('detail-view/<int:pk>/', views.ArticleDetailView.as_view(), name='...'),
- ]
注意这里传入的参数不是 id 而是 pk, 这是视图的要求(也可以传入 slug).pk 是数据表的主键, 在默认情况下其实就是 id.
这就写好了!
也可以添加任何别的功能, 比如统计浏览量 https://www.dusaiphoto.com/article/detail/45/ :
views.py
- ...
- class ArticleDetailView(DetailView):
- ...
- def get_object(self):
- """
- 获取需要展示的对象
- """
- # 首先调用父类的方法
- obj = super(ArticleDetailView, self).get_object()
- # 浏览量 +1
- obj.total_views += 1
- obj.save(update_fields=['total_views'])
- return obj
方法 get_object()的作用是获取需要展示的对象. 首先调用父类方法, 将这个对象赋值给 obj 变量, 然后再对其进行统计浏览量的操作, 最后将对象返回. 相当于在原有的方法中把自己的逻辑 "塞" 了进去.
关于 DetailView 更多特性看这里: 官方文档
编辑
除了能够展示信息, 通用视图还包含 CreateView,UpdateView,DeleteView 等编辑数据的类.
如果要新建文章 https://www.dusaiphoto.com/article/detail/22/ , 则视图可以这么写:
views.py
- from django.views.generic.edit import CreateView
- class ArticleCreateView(CreateView):
- model = ArticlePost
- fields = '__all__'
- # 或者只填写部分字段, 比如:
- # fields = ['title', 'content']
- template_name = 'article/create_by_class_view.html'
创建 create_by_class_view.HTML 文件(目录在哪, 你应该已经很清楚了), 写入:
create_by_class_view.HTML
- <form method="post">{% csrf_token %}
- {{ form.as_p }}
- <input type="submit" value="Save">
- </form>
最后添加 url:
urls.py
- urlpatterns = [
- path('create-view/', views.ArticleCreateView.as_view(), name='...'),
- ]
虽然外观简陋(这不是重点), 但现在这个视图确实已经能够创建新文章了!
UpdateView 和 DeleteView 这里就不再赘述了, 以后用到的地方再进行讲解.
想提前了解的同学戳这里: 官方文档
总结
有没有感受到代码隔离和继承的强大? 没有? 以后的章节会逐渐使用类编写视图, 你会慢慢体会的.
类视图的内容非常丰富, 短短一篇文章只能蜻蜓点水而已. 读者在编程中遇到困难了, 官方文档是你最好的教程.
如果你有耐心从头到尾阅读类视图的官方文档, 那当然是最好的了.
有疑问请在杜赛的个人网站 http://www.dusaiphoto.com 留言, 我会尽快回复.
或 Email 私信我: dusaiphoto@foxmail.com
项目完整代码: Django_blog_tutorial https://github.com/stacklens/django_blog_tutorial
来源: https://juejin.im/post/5c3f305c51882523f026135f