类的继承让代码的复用变得容易,当我们代码的逻辑被重复使用,同时有需要共享的数据,就可以考虑封装一个类来使用.
l View: 基础的View,实现了基于HTTP方法得分发(dispatch)逻辑,比如GET请求会调用对应的get方法,POST请求会调用对应的post方法,但它自己没有实现具体的get或者post方法.
l TemplateView: 继承自View,可以直接用来返回指定的模板.它实现了get方法,可以传递变量到模板中来进行数据展示.
l DetailView: 继承自View,实现了get方法,并且可以绑定某一个模板,用来获取单个实例的数据.
l ListView:继承自View,实现了get方法,可以通过绑定模板来批量获取数据
class MyView(View): def get(self, request): return HttpResponse(‘result‘)
urls.py
1 path(‘view/‘, views.MyView.as_view()),
1 def my_view(request): 2 if request.method == ‘GET‘: 3 return HttpResponse(‘result‘)
使用class-based view来实现的一个明显的好处就是解耦了HTTP的各种请求,在这种情况下,如果我们如果需要给视图新增一个处理POST请求的逻辑,不需要再去修改get函数,只需要新增一个post函数即可,无需去改变已有的逻辑.
而在路由配置中,通过as_view()来接收请求以及返回响应.这个函数会动态获取当前请求HTTP Method对应的方法来处理对应请求,简单理解就是做了if request.method == ‘‘判断并执行对应的操作.
增加了指定模板的功能,可以用来返回某个模板,也可以直接写到URL上,这样返回的是静态页面
也可以继承TemplateView实现get_context_data方法来将要展示的数据传递到模板中.
1 path(‘view/‘, TemplateView.as_view(template_name=‘view.html‘))
1 def get_context_data(self, **kwargs): 2 context = super().get_context_data(**kwargs) 3 context.update({ 4 ‘sidebars‘: SideBar.get_all(), 5 }) 6 context.update(Category.get_navs()) 7 8 return context
DetailView可以理解为也拥有TemplateView的能力,并且可以通过绑定一个模板来指定数据源.
l queryset属性:跟model一样,二选一.设定基础的数据集,model的设定没有过滤的功能,而通过queryset = Post.objects.filter()可以过滤.
l get_queryset接口:用来获取数据,如果设定了queryset则直接返回queryset
l get_object接口:根据URL参数,从queryset上获取对应的实例
l get_context_data接口:获取渲染到模板中的所有上下文,如果有新增数据需要传递,可以重写该方法来实现.
1 class PostDetailView(DetailView): 2 queryset = Post.latest_posts() 3 template_name = ‘detail.html‘ 4 context_object_name = ‘post‘ 5 pk_url_kwarg = ‘post_id‘
这里的pk_url_kwarg是用于接收一个来自url的参数,这个参数将作为查询条件
1 path(‘post/<int:post_id>.html‘, views.PostDetailView.as_view(), name=‘post-detail‘),
与DetailView类似,不过DetailView只获取一条数据,而ListView获取多条数据.因为是列表数据,如果数据类过大的话就没法一次都返回,因此还需要完成分页功能.
1 class IndexView(CommonViewMixin, ListView): 2 queryset = Post.latest_posts() 3 paginate_by = 1 4 context_object_name = ‘post_lists‘ 5 template_name = ‘list.html‘
list.html
1 {% extends ‘base.html‘ %} 2 3 {% block title %} 4 {% if tag %} 5 标签页: {{ tag.name }} 6 {% elif category %} 7 分类页: {{ category.name }} 8 {% else %} 9 Posts 10 {% endif %} 11 {% endblock %} 12 13 {% block main %} 14 <ul> 15 {% for post in post_lists %} 16 <li> 17 <a href="{% url ‘blog:post-detail‘ post.id %}">{{ post.title }}</a> 18 <div> 19 <span>作者:{{ post.owner.username }}</span> 20 <span>分类:{{ post.category.name }}</span> 21 </div> 22 <p>{{ post.desc }}</p> 23 </li> 24 {% endfor %} 25 </ul> 26 {% if page_obj %} 27 {% if page_obj.has_previous %} 28 <a href="?page={{ page_obj.previous_page_number }}">上一页</a> 29 {% endif %} 30 Page {{ page_obj.number }} of {{ paginator.num_pages }}. 31 {% if page_obj.has_next %} 32 <a href="?page={{ page_obj.next_page_number }}">下一页</a> 33 {% endif %} 34 {% endif %} 35 {% endblock %}
既然尝试了使用class-based view,那么接下来就把项目中的function view重构一下吧.在views.py中,我们写了post_list和post_detail两个视图函数
在post_list中,我们处理了多个URL的逻辑,如果使用class-based view的话,可以使用继承来复用代码,将他们拆开.
对于post_list中的所有模板,都会显示分类导航,侧边栏和底部导航,所以我们把这些数据定义为基类来进行代码复用.
1 class CommonViewMixin: 2 def get_context_data(self, **kwargs): 3 context = super().get_context_data(**kwargs) 4 context.update({ 5 ‘sidebars‘: SideBar.get_all(), 6 }) 7 context.update(Category.get_navs()) 8 9 return context
1 class IndexView(CommonViewMixin, ListView): 2 queryset = Post.latest_posts() 3 paginate_by = 5 4 context_object_name = ‘post_lists‘ 5 template_name = ‘list.html‘
1 class CategoryView(IndexView): 2 def get_context_data(self, **kwargs): 3 context = super(CategoryView, self).get_context_data(**kwargs) 4 category_id = self.kwargs.get(‘category_id‘) 5 category = get_object_or_404(Category, pk=category_id) 6 context.update({ 7 ‘category‘: category, 8 }) 9 10 return context 11 12 def get_queryset(self): 13 queryset = super(CategoryView, self).get_queryset() 14 category_id = self.kwargs.get(‘category_id‘) 15 16 return queryset.filter(category_id=category_id)
1 class TagView(IndexView): 2 def get_context_data(self, **kwargs): 3 context = super(TagView, self).get_context_data(**kwargs) 4 tag_id = self.kwargs.get(‘tag_id‘) 5 tag = get_object_or_404(Tag, pk=tag_id) 6 context.update({ 7 ‘tag‘: tag, 8 }) 9 10 return context 11 12 def get_queryset(self): 13 queryset = super(TagView, self).get_queryset() 14 tag_id = self.kwargs.get(‘tag_id‘) 15 16 return queryset.filter(tag_id=tag_id)
get_object_or_404,顾名思义,获取到则返回实例对象,不存在就抛出404错误.
1 class PostDetailView(CommonViewMixin, DetailView): 2 queryset = Post.latest_posts() 3 template_name = ‘detail.html‘ 4 context_object_name = ‘post‘ 5 pk_url_kwarg = ‘post_id‘
1 urlpatterns = [ 2 path(‘category/<int:category_id>/‘, views.CategoryView.as_view(), name=‘category-list‘), 3 path(‘tag/<int:tag_id>/‘, views.TagView.as_view(), name=‘tag-list‘), 4 path(‘post/<int:post_id>.html‘, views.PostDetailView.as_view(), name=‘post-detail‘), 5 path(‘‘, views.IndexView.as_view(), name=‘post_list‘), 6 ]
自此我们的function view 就重构为class-based view,这里介绍一下ListView的GET流程,其他的View大同小异
b) 接着调用get_context_data方法,拿到需要渲染的模板中的数据
ii. 接着调用get_context_object_name拿到要渲染到模板中的这个queryset名称
iii. 然后调用paginate_queryset进行分页处理
c) 调用render_to_response渲染数据到页面中
i. 在render_to_response中调用get_tempalte_names拿到模板名
ii. 把request,context,template_name等传到模板中
接下来要进行页面的一些渲染,毕竟数据是拿到了,页面太丑了也不会有人看=.=
另外就是渲染部分我可能到时候做好直接贴代码上来了,因为前端是真的不知道该怎么说= =
Django开发博客系统(10-使用class-based view)
原文:https://www.cnblogs.com/ylnx-tl/p/12628553.html