中间件总结
调用顺序
- 中间件在settings中注册的时候是一个列表
- 如果我们没有在切点处直接retuen返回,中间件会依次执行。
实验
我们的中间件在settings.py中
MIDDLEWARE = [
'middleware.LearnMiddle.HelloMiddle', # .指的是路径的"/",目录,文件名.类
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
可以看出中间件是一个列表,那么他们内在的关系是什么呢?
我们在LearnMiddleware.py中添加第二个中间件:如下图
此时middleware有两个中间件:
class HelloMiddle(MiddlewareMixin): # Mixin 是使混合的意思
def process_request(self, request):
print(request.META.get('REMOTE_ADDR'))
class TwoMiddle(MiddlewareMixin):
def process_request(self, request):
print('TwoMiddleware')
然后在settings.py中注册新创建的TwoMiddle中间件
(这里是HelloMiddle排在TwoMiddle前面)然后我们随便打开App中的网页,观察到终端
可以看到先执行了HelloMiddle中间件中的打印ip的语句,再执行Twomiddle中间件。
切点
- process_request
- process_view
- process_template_response
- process_response
切面
切面在DJango中和Flask中不是很复杂,因为都已经内置了,如果是JAVA需要自己去实现。
分页
分页属于优化加载,假如一个页面有1万条数据,如果一口气加载会很慢,用户访问时一下子下载1W万条数据,消耗500M流量,这样对用户体验不好,并且对于服务器来说一次取出大量数据,压力很大。
django内置的分页器
提供了分页的工具,存在于django.core中
- Paginator : 数据分页工具
- Page : 具体的某一页面
- Paginator和Page的关系:
QuertSet指的是所有数据的集合,Paginator将集合划成一份一份的Page数据,然后我们调用这些Page就可以了
Paginator:
这里其实就是帮我们做好了封装,我们可以通过调用对象的属性来获取:
- 一共有多少页
- 如何获取某一页
对象创建: Paginator(数据集,每一页数据数)
属性:
count对象总数
num_pages:页面总数
page_range: 页码列表,从1开始
方法: page(整数): 获得一个page对象
实验:分页
在models中建立模型
from django.db import models
class Student(models.Model):
s_name = models.CharField(max_length=16)
s_age = models.IntegerField(default=15)
然后创建迁移文件
再迁移
添加路由
url('^addstudents/', views.add_students, name='add_Students'),
url('^getstudents/', views.get_students, name='get_students'),
接着我们在views中,创建添加数据的函数
def add_students(request):
for i in range(100):
student = Student()
student.s_name = '小明%d' % i
student.s_age = i
student.save()
return HttpResponse('学生创建成功')
然后我们再在views中创建查看数据的函数
def get_students(request):
student = Student.objects.all()
data = {
'students': student
} # 填坑的时候是字典
render(request, 'students.html', context=data)
与此同时我们还要新建模板students.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>students</title>
</head>
<body>
<ul>
{% for student in students %}
<li>{{ student.s_name }}</li>
{% endfor %}
</ul>
</body>
</html>
然后我们先访问addstudents:
然后访问getstudents查看添加的学生姓名:
我们可以看到一页显示了100条数据,现在我们想达到一页只显示10条的数据效果。
原生分页
将views中的get_students改为如下:
def get_students(request):
# 原生分页
page = int(request.GET.get('page', 1)) # 先建立一个page变量,如果没有获取到,默认值为第一页
per_page = int(request.GET.get('per_page', 10)) # 默认每页10条数据
student = Student.objects.all()[per_page*(page - 1):per_page*page] # 后面的[]中的内容指的是:按per_page条为一页,取的是第page页
data = {
'students': student
} # 填坑的时候是字典
return render(request, 'students.html', context=data)
这样在访问页面时提供page,per_page两个参数就能调整一页显示的内容了
原生分页器写起来比较麻烦,并且有效无效还需要自己判断。
使用分页器
新建url
url('^getstudentswithpage/', views.get_students_with_page(), name='get_students_with_page')
views:
def get_students_with_page(request):
# 使用分页器,page,per_page参数照样接收,和原生一样
page = int(request.GET.get('page', 1)) # 先建立一个page变量,如果没有获取到,默认值为第一页
per_page = int(request.GET.get('per_page', 10)) # 默认每页10条数据
students = Student.objects.all()
# 使用分页器,需要直接构造Paginator对象
paginator = Paginator(students, per_page) # students是查询集,per_page是每一页查多少条,还有其他参数,比如是否允许空页面
page_object = paginator.page(page) # 获取到具体的某一页
data = {
'page_object': page_object
}
return render(request, 'students_with_page.html', context=data)
新建模板 students_with_page.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>students_with_page</title>
</head>
<body>
<ul>
{% for student in page_object.object_list%}
<li>{{ student.s_name }}</li>
{% endfor %}
</ul>
</body>
</html>
测试:
可以看到我们可以任意取出想要的页数数据
注意,在模板中对page_object还有很多参数可以调用:
Pagintor的类的属性/方法
我们可以发现page_object对象有很多属性可以调用
- count:有多少页数据
- number:目前是第几页
- has_previous/next:是否有上/下一页
- previous/next_page_number:上/下一页的页码
- object_list:对象的数据列表
- end_index:最后一页页码
- has_other_pages:是否有上一页或者下一页
- index:首页
- paginator:属于哪个分页器
- start_index从哪个page开始的
这就是分页器比原生分页功能更强的地方,在后期我们对网页插入下一页上一页的按钮,或者是页码提供了便捷的方法
**
下面我们在页面中添加页码数,页码数在我们Paginator的类的属性中
我们先查看Pginator有哪些属性和方法:
可以看到有:
- validata_number可用的number
- get_page
- page
- count
- num_pages
page_range:
@property def page_range(self): """ Return a 1-based range of pages for iterating through within a template for loop.意思是说帮你生成了一个从1开始的页码的迭代 """ return range(1, self.num_pages + 1)
page_range就是页码的码表
可以看到这个方法,被一个property装饰器给修饰了
property装饰器:是将一个函数变为一个属性,然后这个属性在操作时我们可以观测它。
接着我门在views中添加页码信息的坑:
对应的我们在html中填坑:
现在我们重新访问网页:
可以看到所有的页码
现在我们再次提高难度,可以点击页码,跳转到对应的页码
html如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>students_with_page</title>
</head>
<body>
<ul>
{% for student in page_object.object_list%}
<li>{{ student.s_name }}</li>
{% endfor %}
</ul>
<ul>
{% for page_index in page_range %}
<li><a href="{% url 'App:get_students_with_page' %}?page={{ page_index }}">{{ page_index }}</a></li>
{% endfor %}
</ul>
</body>
</html>
想要这么做,我们可以用a标签,或者用其他方法实现跳转,比如添加一个click事件
这里我们用a标签来跳转网页(第16行)
{%
`url 'App:get_students_with_page'
%} 这个是url路径<br />
?page={{
page_index `}}
这个是get请求参数
测试
我们可以看到每个页码都可以跳转。
美化网页
导入bootstrap进项目
上面的网页看起来都太low了,现在我们要美化一下,我们使用bootstrap3
现在也有bootstrap4,感兴趣想要了解3和4的看这个:
https://blog.csdn.net/miaomiao_1024/article/details/834183
百度搜索bootstrap cdn 然后我们找到合适的导入进项目
这里介绍一个网站:https://www.bootcdn.cn/twitter-bootstrap/
大家可以选择合适的版本(我使用的是3.3.0),最好不要测试版,(我在用4的时候出现了渲染不出预期效果的bug),复制bootsrap.css的链接,还有js的链接
然后粘贴到模板的head中
<head>
<meta charset="UTF-8">
<title>students_with_page</title>
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.css" rel="stylesheet">
<script type='text/javascript' src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.js"></script>
</head>
但是bootstrap依赖jQuery,我们还要导入jQuery
注意jQuery的链接要放在bootstrap之前
<head>
<meta charset="UTF-8">
<title>students_with_page</title>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.css" rel="stylesheet">
<script type='text/javascript' src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type='text/javascript' src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.js"></script>
</head>
美化页码的索引列表
导入美化的格式
打开bootstrap官网
选择bootstrap3,然后选择组件,再选择分页,把代码嫖下来。
粘贴到html中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>students_with_page</title>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.css" rel="stylesheet">
<script type='text/javascript' src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type='text/javascript' src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.js"></script>
</head>
<body>
<ul>
{% for student in page_object.object_list %}
<li>{{ student.s_name }}</li>
{% endfor %}
</ul>
<ul>
{% for page_index in page_range %}
<li><a href="{% url 'App:get_students_with_page' %}?page={{ page_index }}">{{ page_index }}</a></li>
{% endfor %}
</ul>
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</body>
</html>
24-42行为我们粘贴的样式
将导入的格式套用进项目
将列表的索引页码套用
但是这里的 1 2 3 4 5只是空客,单单的只有样式,我们要把上面实际的跳转链接替换掉这些空壳 1 2 3 4 5数字
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>students_with_page</title>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.css" rel="stylesheet">
<script type='text/javascript' src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type='text/javascript' src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.js"></script>
</head>
<body>
<ul>
{% for student in page_object.object_list %}
<li>{{ student.s_name }}</li>
{% endfor %}
</ul>
<ul>
{% for page_index in page_range %}
<li><a href="{% url 'App:get_students_with_page' %}?page={{ page_index }}">{{ page_index }}</a></li>
{% endfor %}
</ul>
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% for page_index in page_range %}
<li><a href="{% url 'App:get_students_with_page' %}?page={{ page_index }}">{{ page_index }}</a></li>
{% endfor %}
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</body>
</html>
24 -40行将样式套用进项目
将上/下一页的按钮套用
接着我们还要把上一页和下一页的按钮也生效,代码如下
(把之前单独的for循环列出页码列表的代码删掉)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>students_with_page</title>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.css" rel="stylesheet">
<script type='text/javascript' src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type='text/javascript' src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.js"></script>
</head>
<body>
<ul>
{% for student in page_object.object_list %}
<li>{{ student.s_name }}</li>
{% endfor %}
</ul>
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
{# 前一页的按钮,查找的page参数是views中的page_object对象中的previous_page_number属性 #}
<a href="{% url 'App:get_students_with_page' %}?page={{ page_object.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{# 这个for循环替代了之前的空壳,用来显示页码索引 #}
{% for page_index in page_range %}
<li><a href="{% url 'App:get_students_with_page' %}?page={{ page_index }}">{{ page_index }}</a></li>
{% endfor %}
<li>
{# 下一页的按钮 #}
<a href="{% url 'App:get_students_with_page' %}?page={{ page_object.next_page_number}}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</body>
</html>
处理套用时产生的异常
但是,问题来了,当你在第一页时点击上一页,会重定向到index,最后一页点击下一页时也会重定向到Index
说明报错了,原因是没有上一页或者下一页可以查询到,所以我们要处理这个异常。
解决方法:在每一次点击上一页或者下一页的时候都要做判断,判断是否有前一页,或者有后一页(调用了前面提到的paginator类中的has_previous/next属性),如果没有上/下一页:
- 我们就将上/下一页按钮的a herf设置为#(空)
- 我们使用bootstrap中携带的disablesd类将按钮禁止点击
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>students_with_page</title>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.css" rel="stylesheet">
<script type='text/javascript' src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script type='text/javascript' src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.js"></script>
</head>
<body>
<ul>
{% for student in page_object.object_list %}
<li>{{ student.s_name }}</li>
{% endfor %}
</ul>
<nav aria-label="Page navigation">
<ul class="pagination">
{% if page_object.has_previous %} {# 如果存在前一页 #}
<li>
{# 前一页的按钮,查找的page参数是views中的page_object对象中的previous_page_number属性 #}
<a href="{% url 'App:get_students_with_page' %}?page={{ page_object.previous_page_number }}"
aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% else %} {# 如果不存在前一页 #}
<li class="disabled">
<a href="#"
aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
{% for page_index in page_range %} {# 这个for循环替代了之前的空壳,用来显示页码索引 #}
<li><a href="{% url 'App:get_students_with_page' %}?page={{ page_index }}">{{ page_index }}</a></li>
{% endfor %}
{% if page_object.has_next %} {# 如果存在下一页 #}
<li>
<a href="{% url 'App:get_students_with_page' %}?page={{ page_object.next_page_number }}"
aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% else %} {# 如果不存在下一页 #}
<li class="disabled">
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
</body>
</html>
当前所在的页码亮起
我们在循环页码时加一个ifequal判断
{% for page_index in page_range %} {# 这个for循环替代了之前的空壳,用来显示页码索引 #}
{% ifequal page_index page_object.number %} {# 如果页码的索引号,等于当前页面所在页数,就加亮 #}
{# 将li标签的属性添加active,就为加亮 #}
<li class="active"><a
href="{% url 'App:get_students_with_page' %}?page={{ page_index }}">{{ page_index }}</a></li>
{% else %} {# 正常的页码 #}
<li><a href="{% url 'App:get_students_with_page' %}?page={{ page_index }}">{{ page_index }}</a></li>
{% endifequal %}
{% endfor %}
测试
可以看到成功亮起。
这样通过上面的实践,我们就实现了比较完善的分页。
分页作业
当页码多的时候,我们还可以省略中间的页码改成省略号,并且可以加首页,还有尾页的按钮,这个作为作业。
- 页码超过10个的时候,中间的页码使用省略号代替
- 显示的时候只显示前五页和后五页
此处评论已关闭