ElementUI 分页+django rest framework

发布时间:2021-03-25 09:58:23编辑:admin阅读(3193)

    一、概述

    在之前的文章中,链接如下:https://www.cnblogs.com/xiao987334176/p/14313471.html

    介绍了ElementUI 分页,前端请求一次接口,获取所有数据,由ElementUI 分页组件实现分页,也就是说由前端来完成了分页功能。

    但是,在实际项目中,不可能一次性返回所有数据,比如几十万条数据。

     

    比较理想的方案是,前端配合后端,一起来实现分页功能。大概思路如下:

    1. 默认访问api,比如:http://127.0.0.1:8000/api/book/list/ ,接口返回10条数据。

    2. 前端点击页码时,比如第二页,请求接口:http://127.0.0.1:8000/api/book/list/?page=2,这里的page=2,表示当前页码数,接口返回10条数据。

    3. 后面的以此类推,总之,每点击一次,请求一次接口,返回10条数据。

     

    效果如下:

    1.gif

     

    二、前端代码

    test.vue

    <template>
      <div>
        <!-- 表格 -->
        <el-table :data="tableData.list" style="width: 100%">
          <el-table-column
            prop="id"
            label="编号"
            width="120">
          </el-table-column>           <!--设置列标-->
          <el-table-column
            prop="title"
            label="书名"
            width="120">
          </el-table-column>
          <el-table-column
            prop="price"
            label="价格"
            width="120">
          </el-table-column>
          <el-table-column
            prop="pub_date"
            label="出版日期"
            width="200">
          </el-table-column>
          <el-table-column
            prop="publish"
            label="出版社"
            width="120">
          </el-table-column>
        </el-table>
        <!-- 分页器 -->
        <div class="block" style="margin-top:15px;">
          <el-pagination
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :current-page="currentPage"
            :page-sizes="[10, 15, 20, 25]"
            :page-size="pageSize"
            layout="total, prev, pager, next, jumper"
            :total="total">
          </el-pagination>
        </div>
      </div>
    </template>
    
    <script>
      import axios from 'axios'
    
      export default {
        name: "test2",
        data() {
          return {
            tableData: {},
            currentPage: 1, // 当前页码
            total: 20, // 总条数
            pageSize: 2 // 每页的数据条数
          };
        },
        mounted() {
          this.getlivestockInfo(1);
        },
        methods: {
          //每页条数改变时触发 选择一页显示多少行
          handleSizeChange(val) {
            console.log(`每页 ${val} 条`);
            this.currentPage = 1;
            this.pageSize = val;
          },
          //当前页改变时触发 跳转其他页
          handleCurrentChange(val) {
            console.log(`当前页: ${val}`);
            this.currentPage = val;
            this.getlivestockInfo(val);
          },
          // 请求api,获取信息
          getlivestockInfo(num1) {
            var that = this;
            var params = new URLSearchParams();
            params.append('page', num1);
            // console.log("params",params)
            let url = "http://127.0.0.1:8000/api/book/list/?page=" + that.currentPage
            axios.get(url, params)    //补上后台接口即可
              .then(response => {  // 请求成功
                // console.log('请求成功');
                that.tableData = response.data.data;
                that.currentPage = num1;
                that.pageSize = that.tableData.pageSize;
                that.total = that.tableData.total;
                console.log('请求成功, 获取' + that.tableData.list.length + "条数据");
              }).catch(error => {  // 请求失败
              console.log('请求失败');
              console.log(error);
            })
          }
        }
      }
    </script>
    
    <style scoped>
    
    </style>

     

    代码解释:

    <el-pagination
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :current-page="currentPage"
            :page-sizes="[10, 15, 20, 25]"
            :page-size="pageSize"
            layout="total, prev, pager, next, jumper"
            :total="total">
          </el-pagination>

    其中:
    :current-page的值表示当前是第几页;
    :page-sizes的值表示可以选择一页多少条;
    :page-size的值表示当前一页显示几条;
    layout的值表示分页需要显示的内容,例如“total” 表示总数、“next” 表示下一页等;
    :total的值表示共几页;

    因为currentPage、pageSize并不是具体的值,所以需要在script标签中的data()中为其进行赋值。于是在上面说到的slice大家都应该知道其作用了吧。在当所有的值都存在时,在界面上会自动把分的页显示出来,如效果图中的:1、2、3……6

     

    其他代码就不做解释了,注释里面写的比较清楚。


    注意:确保已经安装了ElementUI和axios,根据实际情况配置路由。

     

    三、后端代码

    这里以django 3.1.5为后端

     

    安装模块

    pip3 install django-cors-headers djangorestframework

     

    新建一个项目:paging_demo

    1.png

     

    修改paging_demo/settings.py

    注册corsheaders和channels,corsheaders主要是用来解决跨域问题的。

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'api.apps.ApiConfig',
        'corsheaders',  # 注册应用cors
    ]

     

    注册中间件

    MIDDLEWARE = [
        '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',
        'corsheaders.middleware.CorsMiddleware',  # 注册组件cors
    ]

     

    最后一行增加以下内容

    REST_FRAMEWORK = {
        'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",
        'DEFAULT_VERSION': 'v1',
        'ALLOWED_VERSIONS': ['v1', 'v2'],
        'VERSION_PARAM': 'version',
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE':2  # 默认分页大小
    }
    
    #跨域增加忽略
    CORS_ALLOW_CREDENTIALS = True
    CORS_ORIGIN_ALLOW_ALL = True
    # CORS_ORIGIN_WHITELIST = (
    #     '*'
    # )
    
    CORS_ALLOW_METHODS = (
        'DELETE',
        'GET',
        'OPTIONS',
        'PATCH',
        'POST',
        'PUT',
        'VIEW',
    )
    
    CORS_ALLOW_HEADERS = (
        'XMLHttpRequest',
        'X_FILENAME',
        'accept-encoding',
        'authorization',
        'content-type',
        'dnt',
        'origin',
        'user-agent',
        'x-csrftoken',
        'x-requested-with',
        'Pragma',
    )

    注意:PAGE_SIZE 根据实际情况修改

     

    修改paging_demo/urls.py

    增加路由

    from django.contrib import admin
    from django.urls import path
    from api import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/book/list/', views.BookView.as_view({'get':'list'}),name='books_list'),
    ]

     

    修改api/views.py

    from django.shortcuts import render
    from rest_framework.views import APIView
    import json
    from rest_framework.viewsets import ViewSetMixin
    from django.shortcuts import render, HttpResponse
    from rest_framework.response import Response
    from rest_framework import status
    from django.http import JsonResponse
    from api import models
    from api.serializers.book import BookSerializer
    from rest_framework.pagination import PageNumberPagination
    from django.db import transaction
    from paging_demo import settings
    
    # 书籍
    class BookView(ViewSetMixin, APIView):
        """书籍"""
        # 查看书籍列表
        def list(self, request, *args, **kwargs):
            queryset = models.Book.objects.all()
            serializer_class = BookSerializer
            # 排序
            queryset = queryset.order_by('id')
            # 分页
            page = PageNumberPagination()
            course_list = page.paginate_queryset(queryset, self.request, self)
            # 分页之后的结果执行序列化
            ser = serializer_class(instance=course_list, many=True)
            data = ser.data
            # print("data",data)
            if not data:
                return JsonResponse({'status': status.HTTP_501_NOT_IMPLEMENTED, 'data': data, 'msg': '数据为空'},
                                    status=status.HTTP_501_NOT_IMPLEMENTED)
            
            # 封装返回数据格式
            data = {
                'list':data,
                'pageSize': settings.REST_FRAMEWORK['PAGE_SIZE'],
                'total':queryset.count()
            }
    
            return JsonResponse({'status': status.HTTP_200_OK, 'data': data},
                                status=status.HTTP_200_OK)

     

    修改api/models.py

    from django.db import models
    
    # Create your models here.
    class Book(models.Model):
        title = models.CharField(max_length=32, unique=True,verbose_name="名称")
        price = models.DecimalField(max_digits=8, decimal_places=2,verbose_name="价格")
        pub_date = models.DateField(verbose_name="出版日期")
        publish = models.CharField(max_length=32, verbose_name="出版社")

     

    在api目录下,创建文件夹serializers,并在此文件下创建book.py

    # !/usr/bin/python3
    # -*- coding: utf-8 -*-
    from rest_framework import serializers
    from api import models
    
    
    class BookSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = models.Book
            fields = '__all__'


    生成数据库,使用以下命令:

    python manage.py makemigrations
    python manage.py migrate

     

    默认使用的是sqlite3数据库,使用Navicat软件打开数据库,使用以下命令插入数据:

    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (1, 'python 高级开发实战', 98.63, '2020-05-06', '工业出版社', 1);
    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (2, 'python 开发实战', 97.5, '2020-05-05', '工业出版社', 2);
    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (3, '活着', 56, '2020-06-01', '工业出版社', 3);
    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (4, '兄弟', 47, '2020-06-02', '工业出版社', 4);
    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (5, '进化心理学', 52, '2020-06-03', '工业出版社', 5);
    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (6, '包法利夫人', 39, '2020-06-04', '工业出版社', 6);
    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (7, '谈美', 46, '2020-06-05', '工业出版社', 7);
    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (8, '局外人', 43, '2020-06-05', '工业出版社', 8);
    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (9, '傲慢与偏见', 58, '2020-06-07', '工业出版社', 9);
    INSERT INTO "main"."api_book" ("id", "title", "price", "pub_date", "publish", "ROWID") VALUES (10, '变形记', 36, '2020-06-08', '工业出版社', 10);

     

    启动django项目即可

     

    最后,访问vue页面,效果就是本文开始的动态图。

     

    这里说明一下接口调用问题,由于django rest framework使用PageNumberPagination进行分页,它必须是get请求才行。如果使用post,需要修改源码才行。

    前端调用接口,比如:http://127.0.0.1:8000/api/book/list/?page=2。注意:由于PageNumberPagination默认接收分页参数为page,因此前端这里必须是page。

    如果不是page,需要对PageNumberPagination进行手动封装才行。

     

     

    本文参考链接:

    https://blog.csdn.net/weixin_46214344/article/details/104051480


关键字