「Django框架」-类视图函数CBV
suiw9 2024-12-01 04:01 52 浏览 0 评论
本文来源于公众号【Python野路子】
函数视图
前面我们学习中视图都是通过定义函数来实现的,例如:
# views.py
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
username = request.POST.get('username')
pwd = request.POST.get('pwd')
if username and pwd:
res['user'] = username
return JsonResponse(res, json_dumps_params={"ensure_ascii": False})
res['msg'] = '用户名或密码错误'
return JsonResponse(res, json_dumps_params={"ensure_ascii": False})
# *************************
# urls.py
from .views import login
urlpatterns = [
path('login/', login, name='login')
]
类视图
类视图引入
上面是通过函数的形式定义的视图,也就是视图函数,这种通过函数实现的便于理解,但是如果一个视图函数对应的URL支持多种HTTP请求方式GET/POST/DELETE/PUT,那可能要在一个函数中写不同的业务逻辑代码,这样会导致可读性和复用性低,我们可以使用类视图class-based view来解决。
Django除了使用函数作为视图,也可以使用类作为视图。使用类视图可以使用类的一些特性,比如继承等。
Django提供了下面几个 class-based view。
View视图
django.views.generic.base.View是主要的类视图,所有的类视图都是继承自它,它实现了基于HTTP方法的分发dispatch逻辑,比如GET请求会调用对应的get方法,POST请求会调用对应的post方法,但它自己没有实现具体的get或者post方法。
如果我们自定义类视图,也可以继承自它,然后再根据当前请求方式method,来实现不同的方法,可以看下其源代码了解下。
类视图,不同的请求方式,有不同的业务处理逻辑,定义不同的方法,方法直接采用HTTP的请求方式名字,作为方法名,例如:
from django.views.generic import View
class LoginView(View):
def get(self, request, *args, **kwargs):
return render(request, 'login.html')
def post(self, request, *args, **kwargs):
username = request.POST.get('username')
pwd = request.POST.get('password')
print(username, pwd)
res = {}
if username and pwd:
res['user'] = username
return JsonResponse(res, json_dumps_params={"ensure_ascii": False})
res['msg'] = '用户名或密码错误'
return JsonResponse(res, json_dumps_params={"ensure_ascii": False})
3)类视图定义完后,要想访问这个类视图,还应该在urls.py中进行映射,而类视图不能像我们访问视图函数那样进行映射,还需要调用View的类方法as_view()方法才能进行映射。
from .views import LoginView
urlpatterns = [
# 类视图:注册 as_view()可以将类视图转换成视图,并决定如何分发请求
path('login/', LoginView.as_view(), name='login')
]
通过上面源码可知,最终还是调用一个函数。
除了get和post方法,View还支持以下方法['get','post','put','patch','delete','head','options','trace']。
如果类视图里面只出现了post方法,这样就只能使用POST方法来访问这个类视图了,如:
class LoginView(View):
def post(self, request, *args, **kwargs):
return HttpResponse("登录成功!")
def http_method_not_allowed(self, request, *args, **kwargs):
return HttpResponse("您当前请求的方式method是:{},本视图只支持使用POST请求!".format(request.method))
而用户访问的页面使用的是get方法,那么就会把这个请求转发给http_method_not_allowed(request,*args,**kwargs)。
其实不管是get请求还是post请求,都会走dispatch(request,*args,**kwargs)方法,所以如果实现这个方法,将能够对所有请求都处理到。
通过上面的function view和class-based view,明显的好处就是,解耦了GET、POST 请求以及其他请求 。
View无论是函数形式还是类形式,都是用来处理HTTP请求的 。因此,对于同一个URL需要处理多种请求的情况,class-based view显然更加合适,因为可以避免写很多分支语句,代码可读性好。
类视图相对于函数视图有更高的复用性,如果其他地方需要使用到某个类的某个特定方法,直接继承该类的视图就可以了。
使用类视图是django推荐的做法,熟悉了类视图的使用方法后,能够减少视图函数的重复代码,节省开发时间。
高级玩法
我们一般在逛一些论坛或者博客,都会发现,首页都是展示一系列的文章列表之类。处理过程非常类似,首先从数据库取出文章列表,然后将这些数据传递给模板并渲染模板。于是,Django把这些相同的逻辑代码抽取了出来,写成了一系列的通用视图函数,即基于类的通用视图(Generic Class Based View)。
TemplateView
django.views.generic.base.TemplateView,这个类视图是专门用来返回模版的。在这个类中,有两个属性是经常需要用到的,一个是template_name,这个属性是用来存储模版的路径,TemplateView会自动的渲染这个变量指向的模版。另外一个是get_context_data,这个方法是用来返回上下文数据的,也就是在给模版传的参数的。
在一个网站中,有一些页面不需要我们从数据库中提取数据到前端页面中,例如网址中的“关于我们” 这个页面一般都是在HTML中写死的数据,不需要进行改动,这个时候我们就可以直接在urls中直接渲染html文件,而不用视图函数或者视图类来进行渲染。
about.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是【关于我们】的页面
</body>
</html>
既可以用来返回某个模板, 也可以直接写到 URL 上:
from django.views.generic import TemplateView
# 如果渲染的模板不需要传递任何参数,那么建议在urls中使用TemplateView直接进行渲染
path('about/',TemplateView.as_view(template_name='about.html')),
这只是简单用法,用来返回静态页面,如果我们想使用TemplateView进行渲染模板,又想传递参数,我可以定义一个类,继承 TemplateView,然后实现它的get_context_data 方法来将要展示的数据传递到模板中 。
about.html:
<body>
这是【关于我们】的页面
<p>
姓名:{{ username }}
</p>
</body>
views.py:
from django.views.generic import TemplateView
class AboutView(TemplateView):
# 指定模板路径
template_name = 'about.html'
# 传入的数据
def get_context_data(self, **kwargs):
# 调用父类的get_context_data方法,否则很多方法,上下文数据将不能使用
context = super().get_context_data(**kwargs)
# 然后添加自定义的参数
context.update({'username': 'admin666'})
return context
urls.py:
然后将上面的映射注释掉,添加新的映射。
path('about/', AboutView.as_view())
这里我们就传递了一个参数username过去。
DetailView
我们要查看某一篇文章详情,就是从数据库中获取这篇文章的记录然后渲染模板。一般我们可以这样实现:
# views.py
class ArticleView(View):
def get(self, request, article_id):
article = Article.objects.get(pk=article_id)
print(article)
return render(request, 'article_detail.html', {'article': article})
# urls.py
path('<int:article_id>/', views.ArticleView.as_view(), name='article_detail'),
<!-- article_detail.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ article.title }}</title>
</head>
<body>
<h1>{{ article.title }}</h1>
<div>
<span>分类:{{ article.category.name }}</span>
<span>阅读量:{{ article.read_num }}</span>
</div>
<hr/>
<p>
{{ article.content }}
</p>
</body>
</html>
对于这种需求,对于这种类型的需求Django ,提供了一个 DetailView 类视图。
from django.views.generic import View, DetailView
class ArticleView(DetailView):
model = Article # 指定需要获取哪个Model的数据。
template_name = 'article_detail.html' # 渲染的模板
context_object_name = 'article' # 指定获取的模型列表数据保存的变量名,这个变量会被传递给模板
pk_url_kwarg = 'article_id' # 默认为pk,需要与url的命名组一致。否则报错
#slug_url_kwarg = 'slug'
def get(self, request, *args, **kwargs):
'''
覆写 get 方法的目的是因为每当文章被访问一次,就得将文章阅读量 +1
get 方法返回的是一个 HttpResponse 实例
之所以需要先调用父类的 get 方法,是因为只有当 get 方法被调用后,
才有 self.object 属性,其值为 Article 模型实例,即被访问的文章 article
'''
response = super().get(request, *args, **kwargs)
# 将文章阅读量 +1
# 注意self.object的值就是被访问的文章article
self.object.read_num += 1
self.object.save(update_fields=['read_num'])
# 视图必须返回一个 HttpResponse 对象
return response
# def get_queryset(self):
# '''
# 用来获取数据 。
# 如果设定了 queryset , 则会直接返回 query set 。
# '''
# pass
# def get_object(self, queryset=None):
# '''
# get_objet()方法会取得url中传递进来的参数来获取某个object。比如,article/2,则get_object()会通过Article模型中
# pk =2
# 根据URL参数,从 queryset 上获取到对应的实例 。
# '''
#
# article_id = self.kwargs.get('article_id')
#
# article = super().get_object(queryset=Article.objects.get(pk=article_id))
#
# return article
# def get_context_data(self, **kwargs):
# '''
# 获取憧染到模板中的所有上下文,
# 如果有新增数据需要传递到模板中,可以重写该方法来完成。
# '''
# pass
# urls.py
path('<int:article_id>/', views.ArticleView.as_view(), name='article_detail'),
ListView
在网站开发中,经常会出现需要列出某个表中的一些数据作为列表展示出来。比如文章列表,图书列表等等。在Django中可以使用ListView来帮我们快速实现这种需求。
因为要对表中的数据进行操作,那么我们首先的创建一个模型,然后映射到数据库中去。
为了方便我们进行测试,这里以Tag模型类举例学习(因为这个定义时字段比较少,哈哈~~),
models.py:
class Tag(models.Model):
name = models.CharField(verbose_name='标签名称', max_length=20, unique=True)
class Meta:
db_table = 'tb_tag'
def __str__(self):
return self.name
为了方便测试,首先我们先来进行批量添加数据,在views中新建一个函数视图:用来批量添加数据。
# views.py
from .models import Tag
def addTag(request):
tags = []
for i in range(1, 50):
tag = Tag(name='标签名称:%s'%i)
Tag.objects.bulk_create(tags) # 批量添加数据
return HttpResponse('success')
# urls.py
path('addTag/', views.addTag)
输入网址,执行视图函数,我们就添加了数据到数据库中了。
然后我们新建一个tag_list.html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表</title>
</head>
<body>
<ul>
{% for tag in tags %}
<li>{{ tag.name }}</li>
{% endfor %}
</ul>
</body>
</html>
我们可以这样实现:
class TagListView(View):
def get(self, request):
tags = Tag.objects.all()
return render(request, 'tag_list.html', context={'tags': tags})
首先通过Tag.objects.all()从数据库中获取Tag全部数据,并将其保存到 tags 变量中,前端模板获取渲染即可。
针对这种从数据库中获取某个模型列表数据(比如这里的 Tag列表)的视图,Django专门提供了一个 ListView 类视图。
class TagListView(ListView):
model = Tag # 指定需要获取哪个Model的数据。
template_name = 'tag_list.html' # 视图渲染的模板
context_object_name = 'tags' # 指定获取的模型列表数据保存的变量名,这个变量会被传递给模板。默认是object_list变量
paginate_by = 10 # 每一页需要展示多少条数据
ordering = 'id' # 排序展示
page_kwarg = 'p' # 指定换页面参数,默认page,以GET方式传递参数
输入网址,就能查看效果了,并且当前页面只有10条数据。
而如果我们向访问后面的页面,我们只需要在输入的网址后面以GET的方式添加一个p=x的参数就行了,例如:
上面是常用属性的用法,而我们还有两个比较常用的方法。
get_context_data:获取上下文的数据,并且可以添加自己的参数,并最终将其传入模板。
get_queryset:如果提取数据的时候,并不需要将所有数据都返回,那么可以重写这个方法,将一些不需要展示的数据给过滤掉。
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表</title>
</head>
<body>
<ul>
{% for tag in tags %}
<li>{{ tag.name }}-{{ username }}</li>
{% endfor %}
</ul>
</body>
</html>
#views.py
class TagListView(ListView):
model = Tag # 指定需要获取哪个Model的数据。
template_name = 'tag_list.html' # 视图渲染的模板
context_object_name = 'tags' # 指定获取的模型列表数据保存的变量名,这个变量会被传递给模板。默认是object_list变量
paginate_by = 10 # 每一页需要展示多少条数据
ordering = 'id' # 排序展示
page_kwarg = 'page' # 指定换页面参数,默认page,以GET方式传递参数
# 重写父类的get_context_data方法,添加自己的参数
def get_context_data(self, *, object_list=None, **kwargs):
# 调用父类的get_context_data方法,否则很多方法,上下文数据将不能使用
context = super().get_context_data(**kwargs)
# 然后添加自定义的参数
context['username'] = 'qmpython'
print(context)
return context
def get_queryset(self):
# 重写get_queryset,根据需要进行过滤,若没有重写方法默认返回所有的数据
queryset = super().get_queryset()
tag_id = self.kwargs.get('tag_id')
return queryset.filter(id__gt=tag_id) # id大于2的
# urls.py
path('tagList/<int:tag_id>/', TagListView.as_view())
在类视图ListView中,从URL捕获的路径参数值保存在实例的kwargs属性(是一个字典)里,非路径参数值保存在实例的 args 属性(是一个列表)里。所以使用self.kwargs.get('tag_id') 来获取从 URL 捕获的tag_id 值。然后查询将id>tag_id的数据过滤出来了。
通过上面打印,我们可以看出里面包含了很多东西:
{'paginator': <django.core.paginator.Paginator object at 0x7fd8e89f9d30>, 'page_obj': <Page 1 of 6>, 'is_paginated': True, 'object_list': <QuerySet [<Tag: MySQL>, <Tag: 快速入门>, <Tag: 摆地摊>, <Tag: 朋友圈>, <Tag: 标签名称:1>, <Tag: 标签名称:2>, <Tag: 标签名称:3>, <Tag: 标签名称:4>, <Tag: 标签名称:5>, <Tag: 标签名称:6>]>, 'tags': <QuerySet [<Tag: MySQL>, <Tag: 快速入门>, <Tag: 摆地摊>, <Tag: 朋友圈>, <Tag: 标签名称:1>, <Tag: 标签名称:2>, <Tag: 标签名称:3>, <Tag: 标签名称:4>, <Tag: 标签名称:5>, <Tag: 标签名称:6>]>, 'view': <django_project.views.TagListView object at 0x7fd8e8a78c88>, 'username': 'qmpython'}
有我们常用的paginator类、page_obj类和我们自定义的一些参数等。
paginator类、page_obj类我们接下来进行学习。
我们来具体看一下 ListView 的流程,其他的 View 大同小异 。(1)请求到达之后,首先会调用 dispatch 进行分发 。(2)接着会调用 get 方法 。①在 GET 请求中,首先会调用 get_queryset 方法,拿到数据源 。②接着调用 get_context_data 方法 , 拿到需要渲染到模板中的数据 。1)在 get _ context data 中,首先会调用 get_paginate_by 拿到每页数据 。2)接着调用 get_context_object_name。3 )然后调用paginate_queryset 进行分页处理 。4)最后拿到的数据转为dict并返回 。③调用 render_to_response 谊染数据到页面中 。1)在render_to_response 中调用 get_tempalte_names 拿到模板名 。2)然后把 request 、context、template_name 等传递到模板中 。
Paginator和Page
Paginator和Page类都是用来做分页的。他们在Django中的路径为django.core.paginator.Paginator和django.core.paginator.Page。
1、Paginator常用属性和方法:
1)count:总共有多少条数据。
2)num_pages:总共有多少页。
3)page_range:页面的区间,即页码列表。比如有3页,那么就range(1,4)。
2、Page类的常用属性和方法:
1)has_next:是否还有下一页。
2)has_previous:是否还有上一页。
3)next_page_number:下一页的页码。
4)previous_page_number:上一页的页码。
5)number:当前页。只有这一个是属性,其他的都是方法。
6)start_index:当前这一页的第一条数据的索引值。
7)end_index:当前这一页的最后一条数据的索引值。
基本使用
我们来看下:
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
class TagListView(View):
def get(self, request):
tags = Tag.objects.all()
# 实例化一个Paginator对象,传入一个需要分页的列表对象以及每页显示几条数据
paginator = Paginator(tags, 2)
print(paginator.count) # 获取总共有多少条数据。55
print(paginator.num_pages) # 获取总有多少页。 6
print(paginator.page_range) # 获取页数的范围,要前不要后 range(1, 7)
page1 = paginator.page(1) # 第1页的page对象
print(type(page1), page1) # <class 'django.core.paginator.Page'> <Page 1 of 6>
# 显示某一页数据的两种方式
for i in page1: # 遍历第1页的所有数据对象
print(i)
print(page1.object_list) # 第1页的所有数据
page2 = paginator.page(2)
print(page2.number) # 当前页,只有这一个是属性,其他的都是方法
print(page2.start_index()) # 当前这一页的第一条数据的索引值。
print(page2.end_index()) # 当前这一页的最后一条数据的索引值。
print(page2.has_next()) # 是否还有下一页。
print(page2.has_previous()) # 是否还有上一页。
print(page2.next_page_number()) # 下一页的页码。判断是否还有下一页,否则报错
print(page2.previous_page_number()) # 上一页的页码。判断是否还有上一页,否则报错
# 抛错
# page=paginator.page(12) # error:EmptyPage
# page=paginator.page("z") # error:PageNotAnInteger
current_page = int(request.GET.get('page', 1))
# 如果页数十分多时,页码导航就会显示比较长,我们可以始终选择当前页码数左边固定显示多少。
if paginator.num_pages > 11: # 11表示只显示多少个页码数,即如果总共28页,如果只显示11个页码, # 如果我们要当前页左右最多显示5个,则进行判断
if current_page - 5 < 1: # 当前页码<5,显示11个
page_range = range(1, 12) # 显示11个
elif current_page + 5 > paginator.num_pages: # 当前页码右边没5个,则
page_range = range(current_page - 5, paginator.num_pages + 1)
else:
page_range = range(current_page - 5, current_page + 5)
else:
page_range = paginator.page_range
try:
page_obj = paginator.page(current_page) # 获取current_page页page对象
except PageNotAnInteger:
# 如果用户请求的页码号不是整数,显示第1页
page_obj = paginator.page(1)
except EmptyPage:
# 如果用户请求的页码号超过了最大页码号,显示最后一页
page_obj = paginator.page(paginator.num_pages)
context = {
'current_page': current_page,
'page_obj': page_obj,
'page_range': page_range
}
return render(request, 'tag_list.html', context=context)
模板tag_list.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
{% for tag in page_obj %}
<li>{{ tag.name }}</li>
{% endfor %}
</ul>
<ul class="pagination" id="pager">
{% if page_obj.has_previous %}
<li class="previous"><a href="?page={{ page_obj.previous_page_number }}">上一页</a></li>
{% else %}
<li class="previous disabled"><a href="#">上一页</a></li>
{% endif %}
{% for page_num in page_range %}
{% if page_num == current_page %}
<li class="item active"><a href="?page={{ page_num }}">{{ page_num }}</a></li>
{% else %}
<li class="item"><a href="?page={{ page_num }}">{{ page_num }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="next"><a href="?page={{ page_obj.next_page_number }}">下一页</a></li>
{% else %}
<li class="next disabled"><a href="#">下一页</a></li>
{% endif %}
</ul>
</body>
</html>
使用通用视图ListView自带分页功能
这个一般是在视图函数中使用分页的代码逻辑,既然现在我们学习了类视图,那我们就使用类视图提供的一些类视图封装好的功能,稍微配置下即可使用,而类视图 ListView 已经帮我们写好了上述的分页逻辑,我们只需通过指定 paginate_by 属性来开启分页功能即可,即在类视图中指定 paginate_by 属性的值:
class TagListView(ListView):
model = Tag # 指定需要获取哪个Model的数据。
template_name = 'tag_list.html' # 视图渲染的模板
context_object_name = 'tags' # 指定获取的模型列表数据保存的变量名,这个变量会被传递给模板。默认是object_list变量
paginate_by = 3 # 每一页需要展示多少条数据
ordering = 'id' # 排序展示
page_kwarg = 'page' # 指定换页面参数,默认page,以GET方式传递参数
# 重写父类的get_context_data方法,添加自己的参数
def get_context_data(self, *, object_list=None, **kwargs):
# 先继承父类的get_context_data方法,否则很多方法将不能使用
context = super().get_context_data(**kwargs)
# 然后添加自定义的参数
context['username'] = 'qmpython'
#print(context)
# paginator = context.get('paginator') # paginator类
# print(paginator.count) # 获取总共有多少条数据。55
# print(paginator.num_pages) # 获取总有多少页。 6
# print(paginator.page_range) # 获取页数的范围,要前不要后 range(1, 7)
#
# page_obj = context.get('page_obj') # 当前请求页面分页对象。
# print(page_obj.number) # 当前页,只有这一个是属性,其他的都是方法
# print(page_obj.start_index()) # 当前这一页的第一条数据的索引值。
# print(page_obj.end_index()) # 当前这一页的最后一条数据的索引值。
#print(page_obj.has_next()) # 是否还有下一页。
#print(page_obj.has_previous()) # 是否还有上一页。
#print(page_obj.next_page_number()) # 下一页的页码。判断是否还有下一页,否则报错
#print(page_obj.previous_page_number()) # 上一页的页码。判断是否还有上一页,否则报错
return context
# def get_queryset(self):
# # 重写get_queryset,根据需要进行过滤,若没有重写方法默认返回所有的数据
# queryset = super().get_queryset()
# return queryset.filter(id__gt=2) # id大于2的
如果我使用访问第2页即加?page=2,那么
page_obj.number = 2
page_obj.start_index() = 11
page_obj.end_index() = 20
page_obj.has_next() = True
page_obj.has_previous() = True
page_obj.next_page_number() = 3
page_obj.previous_page_number = 1
ListView 传递了以下和分页有关的模板变量供我们在模板中使用:
- paginator ,即 Paginator 的实例。
- page_obj ,当前请求页面分页对象。
- is_paginated,是否已分页。只有当分页后页面超过两页时才算已分页。
- object_list,请求页面的对象列表,和 context_object_name设置为tags等价。所以在模板中循环当前页列表时可以选 tags ,也可以选 object_list,还可以选择page_obj 。
模板文件tag_list.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
{% for tag in tags %}
<li>{{ tag.name }}-{{ qmpython }}</li>
{% endfor %}
</ul>
<ul class="pagination" id="pager">
{% if page_obj.has_previous %}
<li class="previous"><a href="?page={{ page_obj.previous_page_number }}">上一页</a></li>
{% else %}
<li class="previous disabled"><a href="#">上一页</a></li>
{% endif %}
{% for page_num in paginator.page_range %}
{% if page_num == page_obj.number %}
<li class="item active"><a href="?page={{ page_num }}">{{ page_num }}</a></li>
{% else %}
<li class="item"><a href="?page={{ page_num }}">{{ page_num }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="next"><a href="?page={{ page_obj.next_page_number }}">下一页</a></li>
{% else %}
<li class="next disabled"><a href="#">下一页</a></li>
{% endif %}
</ul>
</body>
</html>
Django第三方分页库
使用 Django 内置的 Pagination 只能实现上面的简单分页效果,但通常更加高级的分页效果,当前页面高亮显示,且显示当前页面前后几页的连续页码,始终显示第一页和最后一页的页码,中间可能还有省略号的效果,表示还有未显示的页码。比如像这种:
仅仅使用 Django Pagination 内置的方法,需要自己写分页逻辑,前面我们已经试过进行处理了,但是很低效也不是很满意,那我们可以借助第三方库django-pure-pagination。
1)首先安装
# 进入虚拟环境
workon django_project
# 安装库
pip install django-pure-pagination
2)settings.py文件进行配置
# app应用引用
INSTALLED_APPS = [
'pure_pagination', # 添加第三方分页的模块,这个模块是在django的分页功能上封装的
]
# 一页显示多少条记录
ONE_PAGE_COUNT = 3
# django-pure-pagination提供的配置项,用于个性化配置分页效果
# 如果需要做分页的设置,需要设置成官网里面的,可以把10改成3,把2改成1,参考https://github.com/jamespacileo/django-pure-pagination.git
PAGINATION_SETTINGS = {
'PAGE_RANGE_DISPLAYED': 4, # 分页条当前页前后应该显示的总页数(两边均匀分布,因此要设置为偶数)
'MARGIN_PAGES_DISPLAYED': 2, # 分页条开头和结尾显示的页数(默认为2)
'SHOW_FIRST_PAGE_WHEN_INVALID': True, # 当请求不存在的页面时,显示第一页,而不是404页面
}
基本使用
视图views.py文件
#from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from pure_pagination import Paginator, EmptyPage, PageNotAnInteger
import logging
from django.conf import settings
class TagListView(View):
def get(self, request):
try:
current_page = request.GET.get('page', 1)
print(type(current_page))
except PageNotAnInteger:
# 如果用户请求的页码号不是整数,显示第1页
current_page = 1
tags = Tag.objects.all()
# 实例化一个Paginator分页对象,传入一个需要分页的列表对象以及每页显示几条数据
paginator = Paginator(tags, settings.ONE_PAGE_COUNT, request=request)
# 获取当前页的page对象
tag_page_obj = paginator.page(current_page)
print(tag_page_obj.pages) # <bound method Page.pages of <Page 4 of 19>>
context = {
'tag_page_obj': tag_page_obj,
}
return render(request, 'tag_list.html', context=context)
模板tag_list.html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
{% for tag in tag_page_obj.object_list %} {# 第page分页对象的元素列表,做分页获取数据要调用 .object_list #}
<li>{{ tag.name }}</li>
{% endfor %}
</ul>
<ul class="pagination" id="pager">
{% if tag_page_obj.has_previous %}
<li class="previous"><a href="?page={{ tag_page_obj.previous_page_number }}">上一页</a></li>
{% else %}
<li class="previous disabled"><a href="#">上一页</a></li>
{% endif %}
{% for page_num in tag_page_obj.pages %}
{% if page_num %}
{% ifequal page_num tag_page_obj.number %}
<li class="item active"><a href="#">{{ page_num }}</a></li>
{% else %}
<li class="item"><a href="?page={{ page_num }}">{{ page_num }}</a></li>
{% endifequal %}
{% else %}
<li class="item"><a href="">...</a></li>
{% endif %}
{% endfor %}
{% if tag_page_obj.has_next %}
<li class="next"><a href="?{{ tag_page_obj.next_page_number.querystring }}">下一页</a></li>
{% else %}
<li class="next disabled"><a href="#">下一页</a></li>
{% endif %}
</ul>
</body>
</html>
与通用视图ListView混合使用
那我们也可以使用它和ListView结合使用,修改之前TagListView视图,让它继承 django-pure-pagination 提供的 PaginationMixin,这个混入类将为我们提供上述提到的分页功能。
from pure_pagination import PaginationMixin
class TagListView(PaginationMixin, ListView):
model = Tag # 指定需要获取哪个Model的数据。
template_name = 'tag_list.html' # 视图渲染的模板
paginate_by = 3 # 每一页需要展示多少条数据
ordering = 'id' # 排序展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
{% for tag in page_obj.object_list %}
<li>{{ tag.name }}</li>
{% endfor %}
</ul>
<ul class="pagination" id="pager">
{% if page_obj.has_previous %}
<li class="previous"><a href="?{{ page_obj.previous_page_number.querystring }}">上一页</a></li>
{% else %}
<li class="previous disabled"><a href="#">上一页</a></li>
{% endif %}
{% for page_num in page_obj.pages %}
{% if page_num %}
{% ifequal page_num page_obj.number %}
<li class="item active"><a href="#">{{ page_num }}</a></li>
{% else %}
<li class="item"><a href="?{{ page_num.querystring }}">{{ page_num }}</a></li>
{% endifequal %}
{% else %}
<li class="item"><a href="">...</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="next"><a href="?{{ page_obj.next_page_number.querystring }}">下一页</a></li>
{% else %}
<li class="next disabled"><a href="#">下一页</a></li>
{% endif %}
</ul>
</body>
</html>
给类视图添加装饰器
在开发中,有时候需要给一些视图添加装饰器。如果用函数视图那么非常简单,只要在函数的上面写上装饰器就可以了。但是如果想要给类添加装饰器,那么可以通过以下两种方式来实现:
需求:在访问个人中心页面的时候,如果没有登录,我们就让它跳转到登录页面,登录之后才能访问个人中心。
1. 装饰类视图中的dispatch方法
from django.utils.decorators import method_decorator # 对类视图进行装饰我们一般都使用这个方法。
def login_required(func):
def inner(request, *args, **kwargs):
username = request.GET.get('username')
print('******===>', username)
# 根据是否传参username来判断是否已登录,
if username: # 真实业务场景可能是先从数据库查询账号和密码
print(username)
return func(request, *args, **kwargs)
else:
print('None')
return redirect(reverse('user:login'))
return inner
class ProfileView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('个人中心页面')
@method_decorator(login_required) # 等同于 dispatch = login_required(dispatch)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def login(request):
return HttpResponse('登录')
前面我们学过不管是get请求还是post请求,都会走dispatch(request,*args,**kwargs)方法,所以我们需要重写dispatch方法,并且继承至父类的dispatch方法。然后再使用装饰器将dispatch装饰,就达到了对类视图进行装饰的目地了。
2. 直接装饰在整个类上
上面第一种方式,我们已经达到目的了,但是这种方法并不太好,我们实现类视图的时候,绝大多数不会去重写dispatch方法,比如说我使用get请求,那么我就重写get方法就行了,使用post请求,重写post方法,而不用去重写dispatch方法。
@method_decorator(login_required, name='dispatch')
class ProfileView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('个人中心页面')
# @method_decorator(login_required)
# def dispatch(self, request, *args, **kwargs):
# return super().dispatch(request, *args, **kwargs)
这样,我们就不用再重写dispatch方法了,只需要在后面添加一个参数name指定装饰的类中的哪一个方法即可。
当然,如果我们有多个装饰器,我们还可以指定一个列表。
@method_decorator([login_required,xx_required],name='dispatch')
这样,我们就成功的对类视图进行了装饰了,推介大家使用第二种方法,不用重写dispatch方法。
相关推荐
- 10款超实用JavaScript音频库(js播放音频代码)
-
HTML5提供了一种新的音频标签实现和规范用一个简单的HTML对象而无需音频插件来控制音频。这只是一个简单的整合这些新的HTML5音频特征及使用JavaScript来创建各种播放控制。下面将介绍10款...
- PROFINET转Modbus网关——工业协议融合的智能枢纽
-
三格电子SG-PNh750-MOD-221,无缝连接Profinet与Modbus,赋能工业物联产品概述...
- 简单实用的Modbus类库,支持从站和DTU
-
一、简介...
- [西门子PLC] S7-200 SMART PROFINET :通过GSD组态PLC设备
-
从S7-200SMARTV2.5版本开始,S7-200SMART开始支持做PROFINETIO通信的智能设备。从而,两个S7-200SMART之间可以进行PROFINETI...
- Modbus(RTU / TCP)有什么异同(modbus tcp和tcp)
-
Modbus是一种广泛使用的工业自动化通信协议,它支持设备之间的数据交换。Modbus协议有两个主要的变体:ModbusRTU(二进制模式)和ModbusTCP(基于TCP/IP网络的模式)。尽管...
- Modbus通信调试步骤详解(modbus调试工具怎么用)
-
Modbus通信调试步骤详解 Modbus通信分为串口和以太网,无论是串口还是以太网,只要是标准Modbus,就可以用Modbus模拟器进行调试。按以下几步进行调试。...
- 理解Intel手册汇编指令(intel 汇编指令手册)
-
指令格式...
- 「西门子PLC」S7-200 SMART的Modbus RTU通讯
-
S7-200SMART集成的RS485端口(端口0)以及SBCM01RS485/232信号板(端口1)两个通信端口可以同时做MODBUSRTU主站,或者一个做MODBUSRTU主站一个做MO...
- InfiniBand网络运维全指南:从驱动安装到故障排查
-
一、InfiniBand网络概述InfiniBand(直译为“无限带宽”技术,缩写为IB)是一种用于高性能计算的计算机网络通信标准,具有极高的吞吐量和极低的延迟,用于计算机与计算机之间的数据互连。它...
- 一加回归 OPPO,背后的秘密不可告人
-
有这样一个手机品牌,它诞生于互联网品牌。在大众群体看来,它的身世似乎模糊不清,许多人以为它是国外品牌。它的产品定位是极客群体,深受国内发烧友,甚至国外极客玩家喜爱。...
- [西门子PLC] S7-200SMART快速高效的完成Modbus通信程序的设计
-
一、导读Modbus通信是一种被广泛应用的通信协议,在变频器、智能仪表还有其他一些智能设备上都能见到它的身影。本文呢,就把S7-200SMART系列PLC当作Modbus主站,把...
- 狂肝10个月手搓GPU,他们在我的世界中玩起我的世界,梦想成真
-
梦晨衡宇萧箫发自凹非寺量子位|公众号QbitAI自从有人在《我的世界》里用红石电路造出CPU,就流传着一个梗:...
- [西门子PLC] 博途TIA portal SCL编程基础入门:1-点动与自锁
-
一、S7-SCL编程语言简介...
- 工作原理系列之:Modbus(modbus工作过程)
-
MODBUS是一种在自动化工业中广泛应用的高速串行通信协议。该协议是由Modion公司(现在由施耐德电气公司获得)于1979年为自己的可编程逻辑控制器开发的。该协议充当了PLCS和智能自动化设备之间的...
你 发表评论:
欢迎- 一周热门
-
-
Linux:Ubuntu22.04上安装python3.11,简单易上手
-
宝马阿布达比分公司推出独特M4升级套件,整套升级约在20万
-
MATLAB中图片保存的五种方法(一)(matlab中保存图片命令)
-
别再傻傻搞不清楚Workstation Player和Workstation Pro的区别了
-
Linux上使用tinyproxy快速搭建HTTP/HTTPS代理器
-
如何提取、修改、强刷A卡bios a卡刷bios工具
-
Element Plus 的 Dialog 组件实现点击遮罩层不关闭对话框
-
MacOS + AList + 访达,让各种云盘挂载到本地(建议收藏)
-
日本组合“岚”将于2020年12月31日停止团体活动
-
SpringCloud OpenFeign 使用 okhttp 发送 HTTP 请求与 HTTP/2 探索
-
- 最近发表
-
- 10款超实用JavaScript音频库(js播放音频代码)
- Howler.js,一款神奇的 JavaScript 开源网络音频工具库
- PROFINET转Modbus网关——工业协议融合的智能枢纽
- 简单实用的Modbus类库,支持从站和DTU
- [西门子PLC] S7-200 SMART PROFINET :通过GSD组态PLC设备
- Modbus(RTU / TCP)有什么异同(modbus tcp和tcp)
- Modbus通信调试步骤详解(modbus调试工具怎么用)
- 理解Intel手册汇编指令(intel 汇编指令手册)
- 「西门子PLC」S7-200 SMART的Modbus RTU通讯
- InfiniBand网络运维全指南:从驱动安装到故障排查
- 标签列表
-
- dialog.js (57)
- importnew (44)
- windows93网页版 (44)
- yii2框架的优缺点 (45)
- tinyeditor (45)
- qt5.5 (60)
- windowsserver2016镜像下载 (52)
- okhttputils (51)
- android-gif-drawable (53)
- 时间轴插件 (56)
- docker systemd (65)
- slider.js (47)
- android webview缓存 (46)
- pagination.js (59)
- loadjs (62)
- openssl1.0.2 (48)
- velocity模板引擎 (48)
- pcre library (47)
- zabbix微信报警脚本 (63)
- jnetpcap (49)
- pdfrenderer (43)
- fastutil (48)
- uinavigationcontroller (53)
- bitbucket.org (44)
- python websocket-client (47)