中间件总结

调用顺序

  • 中间件在settings中注册的时候是一个列表
  • 如果我们没有在切点处直接retuen返回,中间件会依次执行。
  • 如果我们直接return返回,后续的中间件就不再执行了

    实验

    我们的中间件在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
  • process_exeption

    切面

    切面在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

官网链接:https://www.bootcss.com/

现在也有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">&laquo;</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">&raquo;</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">&laquo;</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">&raquo;</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">&laquo;</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">&raquo;</span>
            </a>
        </li>
    </ul>
</nav>
</body>
</html>

测试

可以看到上下页码都是可以生效的。

处理套用时产生的异常

但是,问题来了,当你在第一页时点击上一页,会重定向到index,最后一页点击下一页时也会重定向到Index


说明报错了,原因是没有上一页或者下一页可以查询到,所以我们要处理这个异常。

解决方法:在每一次点击上一页或者下一页的时候都要做判断,判断是否有前一页,或者有后一页(调用了前面提到的paginator类中的has_previous/next属性),如果没有上/下一页:

  1. 我们就将上/下一页按钮的a herf设置为#(空)
  2. 我们使用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">&laquo;</span>
                </a>
            </li>
        {% else %}  {# 如果不存在前一页 #}
            <li class="disabled">
                <a href="#"
                   aria-label="Previous">
                    <span aria-hidden="true">&laquo;</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">&raquo;</span>
                </a>
            </li>
        {% else %} {# 如果不存在下一页 #}
            <li class="disabled">
                <a href="#" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
        {% endif %}
    </ul>
</nav>
</body>
</html>

测试:

可以看到当到达30页时,再点击下一页,就显示禁止符号

当前所在的页码亮起

我们在循环页码时加一个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个的时候,中间的页码使用省略号代替
  • 显示的时候只显示前五页和后五页
最后修改:2024 年 03 月 13 日
如果觉得我的文章对你有用,请随意赞赏