在写视图的时候,Django除了使用函数作为视图,也可以使用类作为视图。使用类视图可以使用类的一些特性,比如继承等。

View

django.views.generic.View是主要的类视图。

所有的类视图都是继承于他。如果写自己的类视图,也可以继承于他。

然后再根据当前请求的method,来实现不同的方法。

比如这个视图只能使用get的方式来请求,那么就可以在这个类中定义get(self,request,*args,**kwargs)方法。以此类推,如果只需要实现post方法,那么就只需要在类中实现post(self,request,*args,**kwargs)

  1. from django.views import View
  2. class BookDetailView(View):
  3. def get(self,request,*args,**kwargs):
  4. return render(request,'detail.html')

类视图写完后,还应该在urls.py中进行映射,映射的时候就需要调用View的类方法as_view()来进行转换。

  1. urlpatterns = [
  2. path("detail/<book_id>/",views.BookDetailView.as_view(),name='detail')
  3. ]

如果向传递参数,可以如下操作

  1. # urls.py
  2. urlpatterns = [
  3. path('index/<id>', views.Index.as_views(), name = 'index')
  4. ]
  5. # views.py
  6. class Index(View):
  7. def get(self, id, *args, **kwargs):
  8. pass

除了get方法,View还支持以下方法['get','post','put','patch','delete','head','options','trace']

如果用户访问了View中没有定义的方法。比如你的类视图只支持get方法,而出现了post方法,会出现405错误。那么就会把这个请求转发给http_method_not_allowed(request,*args,**kwargs)。示例代码如下:

  1. class Index(View):
  2. def post(self,request,*args,**kwargs):
  3. return HttpResponse("post!")
  4. def http_method_not_allowed(self, request, *args, **kwargs):
  5. return HttpResponse("当前采用的method是:{}".format(request.method))

urls.py中的映射如下:

  1. path("idnex/",views.Index.as_view(),name='add_book')

如果你在浏览器中访问index/,因为浏览器访问采用的是get方法,而index/只支持post方法,因此以上视图会当前采用的method是:GET

其实不管是get请求还是post请求,都会走dispatch(request,*args,**kwargs)方法,所以如果实现这个方法,将能够对所有请求都处理到。

TemplateView

django.views.generic.TemplateView,这个类视图是专门用来返回模版的。

  • 如果渲染的这个模版不需要传递任何的参数,那么建议在urls中使用TemplateView ```python

    urls.py

    from django.vies.generic import TemplateView

urlpatterns = [ path(‘’, TemplateView.as_view(template_name=’about.html’)) ]

  1. 在这个类中,有两个属性是经常需要用到的
  2. - `template_name`,这个属性是用来存储模版的路径,`TemplateView`会自动的渲染这个变量指向的模版。
  3. - `get_context_data`,这个方法是用来返回上下文数据的,也就是在给模版传的参数的。
  4. ```shell
  5. # myapp.views.py
  6. from django.views.generic import TemplateView
  7. class HomePageView(TemplateView):
  8. template_name = "home.html"
  9. def get_context_data(self, **kwargs):
  10. context = super().get_context_data(**kwargs)
  11. context["name"] = "jack"
  12. return context

urls.py中的映射代码如下:

  1. from django.urls import path
  2. from myapp.views import HomePageView
  3. urlpatterns = [
  4. path('', HomePageView.as_view()),
  5. ]

ListView

在网站开发中,经常会出现需要列出某个表中的一些数据作为列表展示出来。比如文章列表,图书列表等等。可以使用ListView

  1. # views.py,跟下面的视图函数是否同一个app看自己需要
  2. from django.views.generic import ListView
  3. class ArticleListView(ListView):
  4. model = Article # 指定模型
  5. template_name = 'article_list.html' # 执行渲染模板
  6. paginate_by = 10 # 指定列表显示多少条数据
  7. context_object_name = 'articles' # 指定这个列表模型在模板中的上下文参数名称
  8. ordering = 'create_time' # 指定这个列表的排序方式
  9. page_kwarg = 'page' # 获取第几页的数据的参数名称(?page=xxx)。默认是`page`。
  10. def get_context_data(self, **kwargs): # 获取上下文的数据。
  11. context = super(ArticleListView, self).get_context_data(**kwargs)
  12. print(context)
  13. return context
  14. def get_queryset(self): # 如果你提取数据的时候,并不是要把所有数据都返回,那么你可以重写这个方法。将一些不需要展示的数据给过滤掉
  15. # 默认 return Article.objects.all()
  16. return Article.objects.filter(id__lte=89) # 过滤

models.py

  1. from django.db import models
  2. class Article(models.Model):
  3. title = models.CharField(max_length=100)
  4. content = models.TextField()
  5. create_time = models.DateTimeField(auto_now_add=True)

views.py

  1. from django.shortcuts import render
  2. from django.http import HttpResponse
  3. from .models import Article
  4. def add_article(request):
  5. articles = []
  6. for x in range(0,102):
  7. article = Article(title='标题: %s'%x, content='内容: %s'%x)
  8. articles.append(article)
  9. Article.objects.bulk_create(articles)
  10. return HttpResponse("yes")

urls.py

  1. urlpatterns = [
  2. path('add/', views.add_article)
  3. path('about/', views.ArticleListView.as_view())
  4. ]

article_list.html

  1. <body>
  2. <ul>
  3. {% for article in articles %}
  4. <li> {{ article.title }} </li>
  5. {% endfor %}
  6. </ul>
  7. </body>

Paginator和Page类

PaginatorPage类都是用来做分页的。他们在Django中的路径为django.core.paginator.Paginatordjango.core.paginator.Page

在上面的ListView的def get_context_data)中的context = super(ArticleListView, self).get_context_data(* *kwargs),print(context)打印出来的键值对就有上述类

Paginator属性

  1. count:总共有多少条数据。
  2. num_pages:总共有多少页。
  3. page_range:页面的区间。比如有三页,那么返回range(1,4)

    1. class ArticleListView(ListView):
    2. model = Article
    3. template_name = "article.html"
    4. paginate_by = 25
    5. context_object_name = "articles"
    6. ordering = "create_time"
    7. page_kwarg = "page"
    8. def get_context_data(self, *, object_list=None, **kwargs):
    9. context = super(ArticleListView, self).get_context_data(**kwargs)
    10. paginator = context.get("paginator") # 键是paginator
    11. print("paginator.count: {}\npaginator.num_pages: {}\npaginator.page_range: {}\n".format(paginator.count, paginator.num_pages, paginator.page_range))
    12. return context
    13. def get_queryset(self):
    14. return Article.objects.all()

  4. 输出

    1. paginator.count: 103
    2. paginator.num_pages: 5
    3. paginator.page_range: range(1, 6)

Page常用属性和方法

  1. has_next():是否还有下一页。
  2. has_previous():是否还有上一页。
  3. next_page_number():下一页的页码。最后一页会抛出异常raise EmptyPage(_('That page contains no results'))
  4. previous_page_number():上一页的页码。 第一页使用会抛出异常raise EmptyPage(_('That page number is less than 1'))
  5. number:当前页。
  6. start_index:当前这一页的第一条数据的索引值。
  7. end_index:当前这一页的最后一条数据的索引值。
  • 代码
  1. class ArticleListView(ListView):
  2. model = Article
  3. template_name = "article.html"
  4. paginate_by = 25
  5. context_object_name = "articles"
  6. ordering = "create_time"
  7. page_kwarg = "page"
  8. def get_context_data(self, *, object_list=None, **kwargs):
  9. context = super(ArticleListView, self).get_context_data(**kwargs)
  10. # paginator = context.get("paginator")
  11. # print("paginator.count: {}\npaginator.num_pages: {}\npaginator.page_range: {}\n".format(paginator.count, paginator.num_pages, paginator.page_range))
  12. # print(context)
  13. page = context.get("page_obj") # 键是page_obj
  14. print(page.has_next())
  15. print(page.has_previous())
  16. print(page.next_page_number())
  17. print(page.previous_page_number())
  18. print(page.number)
  19. print(page.start_index())
  20. print(page.end_index())
  21. return context
  22. def get_queryset(self):
  23. return Article.objects.all()

使用URLl访问附带了查询字符串?page=2因为默认第一页的话page.previous_page_number()会抛异常。

  1. True # 是否有下一页
  2. True # 是否有上一页
  3. 3 # 后一页页数
  4. 1 # 前一页页数
  5. 2 # 第几页
  6. 26 #第二页的第一个索引
  7. 50 #第二页的最后一个索引