7.Django之Ajax与分页器

1. 提交数据方式

请求的方式:
1. 浏览器url         GET请求
2. a便签 href属性    GET请求
3. form表单         GET/POST请求
4. Ajax             GET/POST请求   

2. Ajax

Ajax的作用:异步提交,局部刷新.
优点:在不重新加载整个页面的情况下,可以与服务器交互数据并更新网页中的内容.
应用:
网站的注册,动态的获取用户输入,实时的与后端确定,并实时展示到前端(局部刷新).
80% 前后端交互都是Ajax,模块不一样,内容一样.
Django中使用的不是原生的Ajax.使用jQuery封装之后的版本.
使用Ajax时需要先导入jQuery.
2.1 固定语法
 $.ajax({
     // 1.发送数据的地址
     url:'', // 不写默认向当前url地址栏提交
     // 2.请求方式 get/post
     type:'post',  
     // 3.数据
     data:{'username':'kid', 'password':123},
     // 4. 异步回调机制
     success:function (args){
         """
         在利用ajax进行前后端交互的时候
         后端无论返回什么,都自会被回调函数接受,而不影响页面
         """
    }
 })
只要是通过Ajax访问的,所有返回的数据都被回调函数的参数args接收.
2.2 Ajax交互
简易的计算器:页面上有三个input款,
在前端中输入数字,点击按钮,发送ajax请求.
后端计算结果,在返回给前端动态展示到第三个框中,页面不许刷新.
1. 路由层
    # 工程目录下 urls.py 
    # 0. 计算器
    url(r'^add_ajax/', views.add_ajax),
2. 视图层
# app01 应用下 views.py 
from django.shortcuts import render, HttpResponse, redirect

# 0. 计算器
def add_ajax(request):
    # 0.1 返回页面
    return render(request, 'add_ajax.html')
3. 前端页面


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算器title>
    
    {% load static %}
    <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
    
    <p>
        <input type="text" id="i1"> +
        <input type="text" id="i2"> =
        <input type="text" id="i3">
        <input type="submit" id="btn">
    p>
div>
<script>
    // 0. 获取标签
    $('#btn').click(function () {
        // 1. ajax请求
        $.ajax({
            // 1.1 提交的地址
            url: '',
            // 1.2 提交方式
            type: 'post',
            // 1.3 提交的数据
            data: {'i1': $('#i1').val(), 'i2': $('#i2').val()},
            // 数据发出去了  下面的函数args接收返回的数据

            // 1.4 回调函数
            success: function (args) {
                // 1.5 为第三个input写入值
                $('#i3').val(args)
            }
        })
    })
script>
body>
html>
.val()     获取值
.val(args) 写入值
4. 业务逻辑
from django.shortcuts import render, HttpResponse, redirect


# Create your views here.


# 0. 计算器
def add_ajax(request):
    # 0.2 判断请求方式
    if request.method == 'POST':
        # 0.3 获取post数据
        post_obj = request.POST
        print(post_obj)
        # 

        i1 = post_obj.get('i1')
        i2 = post_obj.get('i2')
        print(i1, type(i1), i2, type(i2))
        # 1  2 

        # 0.4 计算值 做类型转换
        i3 = int(i1) + int(i2)
        # 0.5 直接返回整型
        return HttpResponse(i3)

    # 0.1 返回页面
    return render(request, 'add_ajax.html')

7.Django之Ajax与分页器_第1张图片image-20220313154019085

2.3 数据类型
使用 HttpResponse 返回的数据都会被转为字符串.

7.Django之Ajax与分页器_第2张图片

	// 1.6 打印返回值的属性
	console.log(typeof  args);

image-20220315122321743

数据类型不想被转换为字符串类型的数据,可以发送json格式数据.
在前端在反序列化得到Python 对应 js的数据类型.
后端用HttpResponse返回数据,回调函数需要手动反序列化.
后端用JsonResponse返回的数据,回调函数会自动反序列化.
1.1 前端利用JSON.parse()
	success:function (args){
	data = JSON.parse(args)

		···
1.2 在ajax前配置一个参数 dataType:'json',
    $.ajax({
        ···
        dataType:'json',
        ···
2. JsonResponse 返回数据      
from django.http inport JsonResponse
    return JsonResponse('xx')
返回json格式字符串

7.Django之Ajax与分页器_第3张图片

image-20220315123904640

JsonResponse 返回json格式字符串

7.Django之Ajax与分页器_第4张图片

ajax访问之后一般返回一个字典,js中就是一个对象,直接通过点的方式取值.
字典的格式:
{'code': 200, 'msg':'xxx'}
 响应状态码      响应体数据 ...
 
前端可以通过状态码再进行一轮判断在进行操作...

3. form编码格式

get 请求:请求的数据就是直接放在url后面:
url?username=kid&password=123 字符串
post 请求: 
可以向后端发送post请求的方式:
    1. form表单
    2. ajax请求

编码格式:
    1. urlencoded
    2. formdata
form表单提交数据,默认的数据编码格式是urlencoded
django后端针对符合urlencoded 编码格式的数据都会封装到request.POST中.

form-data 针对普通的值还是解析到request.POST中,而文件则解析解析到request.FILES中.
3.1 urlencoded格式
form表单默认提交的是urlencoded格式数据.
    # urls.py
    # 2. form表单数据格式
    url(r'^form_data', views.form_data),
# views.py
# 1. form表达数据
def form_data(request):
    # 1.1 返回访问页面
    return render(request, 'form_data.html')

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>form表单title>
    
    {% load static %}
    
    <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>

<div>
    <form action="" method="post">
        <p>
            <label for="username">用户:
                <input type="text" id="username" name="username" class="form-control form-group-lg">
            label>
        p>
        <p>
            <label for="password">密码:
                <input type="text" id="password" name="password" class="form-control form-group-lg">
            label>
        p>
        <p>
            <input type="submit" class="btn btn-primary">
        <p>
    form>
div>

body>
html>
0. 打开开发者工具选择网络.
1. url中输入 127.0.0.1:8000/form_data
2. 输入账户密码,向后端发送请求.
3. 在浏览器中查看index的数据格式.

7.Django之Ajax与分页器_第5张图片
7.Django之Ajax与分页器_第6张图片

表单数据:是美化后的数据
真正的数据是
username=kid&password=123

7.Django之Ajax与分页器_第7张图片

3.2 form-data格式
form表单传输文件必须设置的数据格式:multipart/formdata,
后端使用request.FILES接收文件.
0. 在form表单中添加一个input标签 type=file.
1. 设置form表单提交的数据格式 enctype="multipart/form-data
<div>
    <form action="" method="post" enctype="multipart/form-data">

            <label for="file">
                <input type="file" id="file" name="file">
            label>

            <input type="submit" class="btn btn-primary">
    form>
div>
获取文件数据 request.FILES
# 1. form表达数据
def form_data(request):
    # 1.2 判定请求方式
    if request.method == 'POST':
        # 1.3 获取POST 请求中的文件数据
        file_obj = request.FILES
        print(file_obj)
        # 1.4 获取表单的数据
        print(request.POST)

    # 1.1 返回访问页面
    return render(request, 'form_data.html')
<MultiValueDict: {'file': []}>
<QueryDict: {'username': ['kid'], 'password': ['123']}>

7.Django之Ajax与分页器_第8张图片7.Django之Ajax与分页器_第9张图片

浏览器中显示的格式
Conten t-Type: multipart/form-data; boundary=----WebKitFormBoundaryDRlTkP2zKWD57WTZ

将enctype="multipart/form-data"去掉
浏览器中显示的格式  Content-Type: application/x-www-form-urlencoded

7.Django之Ajax与分页器_第10张图片

提交文件的时候 request.FILES 获取不到文件, 
<MultiValueDict: {}>

request.POST 获取到文件的文件名

7.Django之Ajax与分页器_第11张图片

4. Ajax提交数据

4.1 urlencoded格式
Ajax提交方式选择 post
现在input框是在form表单中, 按下submit按钮触发绑定事件发送了Ajax之后
,还会提交form表单的请求.

在ajax中阻止submit的后续事件.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>form表单title>
    
    {% load static %}
    
    <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>

<div>
    <form action="" enctype="multipart/form-data">
        <p>
            <label for="username">用户:
                <input type="text" id="username" name="username" class="form-control form-group-lg">
            label>
        p>
        <p>
            <label for="password">密码:
                <input type="text" id="password" name="password" class="form-control form-group-lg">
            label>
        p>
        <p>
            <label for="file">
                <input type="file" id="file" name="file">
            label>
        p>
        <p>
            <input type="submit" class="btn btn-primary">
        <p>
    form>
div>
<script>
    // 0. 绑定事件
    $(':submit').on('click', function () {
        // 1. 获取input表单的数据
        var username = $(':text').val()
        var password = $(':password').val()
        var file = $(':file').val()
        // 2. 阻止后续事件 --> form表单提交谁
        event.preventDefault();
        // 3. ajax 发送数据
        $.ajax({
            // 3.1 提交地址
            url: '',
            // 3.2 提交方式
            type: 'post',
            // 3.3 提交的数据
            data: {username: username, password: password, file: file},
            // 3.4 回调函数
            success: function (args) {
                alert(args)
            }
        })
    })
script>
body>
html>
<MultiValueDict: {}>
<QueryDict: {'username': ['kid'], 'file': ['C:\\fakepath\\bird.png']}>

7.Django之Ajax与分页器_第12张图片

浏览器中查看格式:
Content-Type: application/x-www-form-urlencoded
默认的编码格式也是urlencoded.Ajax提交POST请求,解析到request.POST中.
4.2 json 格式
ajax发送json格式注意点:
1. contenTYPE参数指定为application/json
2. 数据是真正的json格式数据
3. django后端将json格式数据打包到request.body中.
* 前后端交互数据的时候一定要保 验证数据的类型和格式.
    # 3. ajax json提交数据
    url('^ajax_json', views.ajax_json)
后端获取数据
request.is_ajax() 判断当前请求是否为ajax请求
json格式的数据存放在requuest.body中.
# 2. ajax json请求
def ajax_json(request):
    # 2.2 判断请求方式
    if request.is_ajax():
        # 2.3 获取用户数据
        ajax_obj = request.body
        print(ajax_obj, type(ajax_obj))

        return HttpResponse("ok")

    # 2.1 返回页面
    return render(request, 'ajax_json.html')
// 指定编码格式
contentType:'application/jsom', 
// 序列化数据
data: JSON.stringify()

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax_jsontitle>
    
    {% load static %}
    
    <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>

<div>
    <form action="" enctype="multipart/form-data">
        <p>
            <label for="username">用户:
                <input type="text" id="username" name="username" class="form-control form-group-lg">
            label>
        p>
        <p>
            <label for="password">密码:
                <input type="text" id="password" name="password" class="form-control form-group-lg">
            label>
        p>
        <p>
            <label for="file">
                <input type="file" id="file" name="file">
            label>
        p>
        <p>
            <input type="submit" class="btn btn-primary">
        <p>
    form>
div>
<script>
    // 0. 绑定事件
    $(':submit').on('click', function () {
        // 1. 获取input表单的数据
        var username = $(':text').val()
        var password = $(':password').val()
        var file = $(':file').val()
        // 2. 阻止后续事件 --> form表单提交谁
        event.preventDefault();
        // 3. ajax 发送数据
        $.ajax({
            // 3.1 提交地址
            url: '',
            // 3.2 提交方式
            type: 'post',
            // 3.3 提交的数据
            data: JSON.stringify({username: username, password: password, file: file}),
            // 3.4 回调函数
            success: function (args) {
                alert(args)
            }
        })
    })
script>
body>
html>
# 返回json格式字符串 手处理反序列化得到对应的数据类型
b'{"username":"kid","file":"C:\\\\fakepath\\\\bird.png"}' <class 'bytes'>
{'username': 'kid', 'file': 'C:\\fakepath\\bird.png'} <class 'dict'>

7.Django之Ajax与分页器_第13张图片

浏览器中查看
Content-Type: application/jsom
4.3 文件
ajax发送文件需要借助于js内置对象ForData.
1.需要利用FormData内置对象

let ForData对象 = new FormData();
// 添加普通的键值对
formDataobj.append('键', );
// 添加文件对象
formDataobj.append('键', $('')[0].files[0])

2.ajax发送文件必须要指定两个参数
contentType:false,  // 不需要任何编码django能自动识别fordata对象
processData:false,  // 告诉浏览器不要对数据进行任何处理

3.django后端能直接识别带formdata对象并将
普通的键值对自动解析并封装到request.POST中文件自动解析并封装到request.FILES中.
    # urls.py
    # 4. ajax_file
    url('^ajax_file', views.ajax_file)
# 3. ajax 提交文件
def ajax_file(request):
    # 3.2 判断是否为 ajax请求
    if request.is_ajax():
        if request.method == 'POST':
            # 3.3 获取数据
            file_obj = request.POST
            print(file_obj, type(file_obj))
            #  

            file = request.FILES.get('file')
            print(file, type(file))
            # bird.png 

            # 3.4 将文件写入到本地的项目目录下
            with open(file.name, mode='wb') as wf:
                for i in file:
                    wf.write(i)
            return HttpResponse('OK')

    # 3.1 返回页面
    return render(request, 'ajax_file.html')


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax_filetitle>
    
    {% load static %}
    
    <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div>
    
    <form action="">
        <p>
            <label for="username">用户:
                <input type="text" id="username" name="username" class="form-control form-group-lg">
            label>
        p>

        <p>
            <label for="username">密码:
                <input type="password" id="username" name="username" class="form-control form-group-lg">
            label>
        p>
        <p>
            <label for="file">
                <input type="file" id="file" name="file">
            label>
        p>
        <p>
            <label for="submit">
                <input type="submit" id="submit" class="btn btn-primary">
            label>
        p>
    form>
div>
<script>
    // 5.绑定事件
    $(':submit').on('click', function () {
        // 5.1 获取表单数据
        var username = $(':text').val()
        var password = $(':password').val()
        var file = $(':file')[0].files[0]


        // 5.2 阻止后续事件
        event.preventDefault();

        // 5.2 生成文件对象
        var file_obj = new FormData()

        // 5.3 对象添加值
        file_obj.append('username', username)
        file_obj.append('password', password)
        file_obj.append('file', file)

        // 5.4 ajax请求
        $.ajax({
            url: '',
            type: 'post',
            // 提交对象
            data: file_obj,
            // 不设置类型,不做数据处理
            contentType: false,  // 不需要任何编码django能自动识别fordata对象
            processData: false,  // 告诉浏览器不要对数据进行任何处理
            success: function (args) {
                // 什么都不做
            }
        })
    })
script>
body>
html>

5. 自动序列化组件

5.1 准备环境
0. 创建一个库设置编码
1. settings.py 中设置数据库的配置 
2. __init__.py 中替换库
3. 创建表
4. 数据库迁移
5. 设置测试文件
6. 录数据
mysql> create database file character set utf8;
Query OK, 1 row affected (0.01 sec)
# settings.py 配置数据库
DATABASES = {
    'default': {
        # 使用的库
        'ENGINE': 'django.db.backends.mysql',
        # ip
        'HOST': '127.0.0.1',
        # 端口
        'POST': 3306,
        # 连接的库名称
        'NAME': 'file',
        # 用户
        'USER': 'root',
        # 密码
        'PASSWORD': 123,
        # 编码
        'CHARSET': 'UTF8'
    }
}
# app01 应用下的__init__.py
import pymysql
pymysql.install_as_MySQLdb()
# models.py 
from django.db import models

# Create your models here.

class User(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()

    gender_choices = (
        (1, '男'),
        (2, '女'),
        (3, '其他'),
    )
    gender = models.IntegerField(choices=gender_choices)
执行数据库迁移命令:
python manage.py makemigrations
python manage.py migrate
from django.test import TestCase

# Create your tests here.
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django7.settings")
    import django
    django.setup()
    from app01 import models

    models.User.objects.create(name='kid', age=18, gender=1)
    models.User.objects.create(name='qz', age=19, gender=2)
    models.User.objects.create(name='qq', age=20, gender=3)   

7.Django之Ajax与分页器_第14张图片

5.2 用户信息查询
0. 127.0.0.1:8000/view_info
1. 路由层中写路由与视图函数的对应关系
2. 视图函数中获取数据库的数据,传递给前端
3. 前端使用模板语法获取数据
    # urls.py
    # 5. view_info
    url('^view_info', views.view_info)
# 4. view_info
def view_info(request):
    # 4.1 导入 models 模块
    from app01 import models
    # 4.2 获取数据中的数据
    queryset_obj = models.User.objects.all()
    print(queryset_obj)
    # 4.3 返回页面 与 数据 render返回的数据必须的字段

    return render(request, 'view_info.html', locals())

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户数据title>
head>
<body>
    <div>
        {% for obj in queryset_obj %}
            <p>{{ obj.name }} {{ obj.age }} {{ obj.gender }}p>
        {% endfor %}
    div>
body>
html>
5.3 接口文档
用户查询功能中,后端获取的表中所有的数据.(数据的格式是列表套字典,字典中存的是数据对象.)

接口:
前后端分离的项目时,
后端开发时一般将数据处理成列表套字段的格式,序列化返回给后端,
再写一个接口文档,告诉前端每个字段代表的意思.
而不是直接放回一个对象.
TypeError:为了允许序列化非字典对象,请将安全参数设置为 False。
解决编码问题和序列化非dict对象,请将safe参数设置为False.   

return JsonResponse(list,safe=False)
# 4. view_info
def view_info(request):
    # 4.1 导入 models 模块
    from app01 import models
    # 4.2 获取数据中的数据
    queryset_obj = models.User.objects.all()
    # 4.3 组成成一个列表套字段的格式
    info_list = []
    for obj in queryset_obj:
        info_list.append({'pk': obj.pk, 'name': obj.name, 'age': obj.age, 'gender': obj.get_gender_display()})

    print(info_list)

    # 4.3 使用JsonResponse返回数据
    from django.http import JsonResponse
    return JsonResponse(info_list, safe=False)
[{'pk': 1, 'name': 'kid', 'age': 18, 'gender': '男'}, {'pk': 2, 'name': 'qz', 'age': 19, 'gender': '女'}, {'pk': 3, 'name': 'qq', 'age': 20, 'gender': '其他'}]

7.Django之Ajax与分页器_第15张图片

5.3 serializers模块
serializers序列化模块
将上述代码简化,序列化并省去封装数据的步奏.
from django.core import serializers
# 4. view_info
def view_info(request):
    # 4.1 导入 models 模块
    from app01 import models
    # 4.2 获取数据中的数据
    queryset_obj = models.User.objects.all()
	
	# 4.3 使用serializers序列化模块
    from django.core import serializers
    queryset_obj = serializers.serialize('json', queryset_obj)

    print(queryset_obj)

    # 4.3 已经序列化了直接使用HttpResponse返回数据数据
    return HttpResponse(queryset_obj, safe=False)
[{"model": "app01.user", "pk": 1, "fields": {"name": "kid", "age": 18, "gender": 1}}, {"model": "app01.user", "pk": 2, "fields": {"name": "qz", "age": 19, "gender": 2}}, {"model": "app01.user", "pk": 3, "fields": {"name": "qq", "age": 20, "gender": 3}}]

7.Django之Ajax与分页器_第16张图片

接口文件: 写一些描述信息.
[
  {
    "model": "app01.user",
    "pk": 1,
    "fields": {
      "name": "kid",
      "age": 18,
      "gender": 1
    }
  },
......
]

1对应男
2对应女
* 针对 choices 参数 serialize 获取的是编号不是真实的值
···

6. 二次确认操作

6.1 sweetalert
ajax结合sweetalert实现二次确认.

sweetalert网址:

http://lipis.github.io/bootstrap-sweetalert/

需要 sweetalert框架的文件.
6.2 提交主键&刷新数据
向后端发送ajax请求删除数据,需要携带数据的id:

方式1: url/数据的主键
url:'/delete/user' + currentBtn.attr('del_id'),  // 传递主键值方式1

方式2: 放在请求体内
data:{'del_id': currentBtn.attr('del_id')},

删除数据后剧本刷新:
方式1:
window.location.reload()
方式2:
利用DOM操作动态刷新 当前标签的父标签的父标签
操作对象.parent().parent().remove()
6.3 存在的问题

    <style>
        div.sweetalert h2{
            padding-top: 10px;
        }
    style>

7.Django之Ajax与分页器_第17张图片

7.Django之Ajax与分页器_第18张图片

6.4 注意点
自己写的bug

7.Django之Ajax与分页器_第19张图片

7.Django之Ajax与分页器_第20张图片

6.5 完整代码
    # urls.py
    # 5. view_info
    url('^view_info/', views.view_info),
    # 6. 删除数据
    url('^delete_user/', views.delete_user),
# views.py
# 4. view_info
def view_info(request):
    # 4.1 导入 models 模块
    from app01 import models
    # 4.2 获取数据中的数据
    queryset_obj = models.User.objects.all()
    # 4.3 返回页面
    return render(request, 'view_info.html', locals())


# 5. 删除用户
def delete_user(request):
    # 5.1 获取提交方式
    if request.is_ajax():
        # 5.2 获取id
        delete_id = request.POST.get('del_id')

        # 5.3 删除数据
        from app01 import models
        # delete 删除会返回操作影响的数量  (1, {'app01.User': 1})
        delete_num = models.User.objects.filter(pk=delete_id).delete()
        print(delete_num)
        # 5.4 组织一个字典返回
        if delete_num[0] == 0:
            data = {'code': 4000, 'msg': '没有对应的数据'}
        else:
            data = {'code': 2000, 'msg': '删除成功'}

        # 5.5 返回一个json对象
        from django.http import JsonResponse
        return JsonResponse(data)


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户数据title>
    
    {% load static %}
    
    <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
    
    <script src="{% static 'dist/sweetalert.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
head>
<body>
<div>
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <table class="table-striped table table-hover">
                <thead>
                <tr>
                    <th>idth>
                    <th>姓名th>
                    <th>年龄th>
                    <th>性别th>
                    <th>操作th>
                tr>
                thead>
                <tbody>
                {% for foo in queryset_obj %}
                    <tr>
                        <td>{{ foo.pk }}td>
                        <td>{{ foo.name }}td>
                        <td>{{ foo.age }}td>
                        <td>{{ foo.get_gender_display }}td>
                        <td>
                            <a class="btn btn-primary btn-sm">0.0a>
                            
                            <a class="btn btn-danger btn-sm del" del_id="{{ foo.pk }}">删除a>
                        td>
                    tr>
                {% endfor %}
                tbody>
            table>
        div>
    div>
div>
<script>

    //  为按键绑定二次弹框
    $('.del').on('click', function () {
        // 获取当前操作对象
        delete_obj = $(this)
        swal({
                title: "你确定要删除吗?",
                text: "会将你的数据全部清除",
                type: "warning",  // 不要修改
                showCancelButton: true,
                confirmButtonClass: "btn-danger",  // 颜色
                confirmButtonText: "确定",
                cancelButtonText: "取消!",
                showLoaderOnConfirm: true,  // 显示等待
                closeOnConfirm: false,
                closeOnCancel: false 	
            },
            function (isConfirm) {


                if (isConfirm) {  // 判断按下的按键是确定还是取消
                    // 按下确定之后发生ajax请求
                    $.ajax({
                        // 设置提交的地址
                        url: '/delete_user/',
                        type: 'post',
                        data: {'del_id': delete_obj.attr('del_id')},
                        success: function (args) {
                            if (args.code === 2000) {
                                // 延时
                                setTimeout(function () {
                                    swal("确定", args.msg, "success");
                                    //  删除后清除该行的数据
                                    delete_obj.parent().parent().remove()
                                }, 2000);

                            } else if (args.code === 4000) {
                                swal("异常", args.msg, "error");
                            }
                        }
                    })
                } else {
                    swal("取消", "你已经撤销操作:)", "success");
                }
            });
    })

script>
body>
html>

7. 批量插入数据

# 1. 图书列表1
class Book1(models.Model):
    # 1.1 id 字段自动生成
    # 1.2 书名
    title = models.CharField(max_length=32, verbose_name='书名')


# 2. 图书列表2
class Book2(models.Model):
    # 2.1 id 字段自动生成
    # 2.2 书名
    title = models.CharField(max_length=32, verbose_name='书名')
执行数据库迁移命令:
python manage.py makemigrations
python manage migrate
    # 9. 大量插入1
    url('^add_info1', views.add_info1),
    # 10. 批量插入2
    url('^add_info2', views.add_info2),
from app01 import models


# 8. add_info1
def add_info1(request):
    # 8.1 导入时间模块
    import time
    old_time = time.time()
    # 8.2 操作数据库1000次
    for i in range(1000):
        models.Book1.objects.create(title=f'第{i}本书')
    new_time = time.time()
    # 8.3 计算时间
    print('数据写入时间: %s' % (new_time - old_time))

    # 8.4 读取数据
    query_set = models.Book1.objects.all()
    return render(request, 'book_list.html', locals())


# 9. add_info2
def add_info2(request):
    # 9.1 导入时间模块
    import time
    old_time = time.time()
    # 9.2 生成器
    book_list = []
    for i in range(1000):
        book_obj = models.Book2(title=f'第{i}本书')
        book_list.append(book_obj)
    # 9.3 一次全部写入
    models.Book2.objects.bulk_create(book_list)
    # 9.4 计算时间
    new_time = time.time()
    print('数据写入时间: %s' % (new_time - old_time))

    # 9.5 读取数据
    query_set = models.Book2.objects.all()
    return render(request, 'book_list.html', locals())

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>图书列表title>
head>
<body>
<table>
    <thead>
    <tr>
        <th>idth>
        <th>titleth>
    tr>
    thead>
    <tbody class="form-control form-group-lg">
    {% for foo in query_set %}
        <tr>
            <td>{{ foo.pk }}td>
            <td>{{ foo.title }}td>
        tr>
    {% endfor %}

    tbody>
table>
body>
html>
orm内置bulk_create能够减少操作时间.

7.Django之Ajax与分页器_第21张图片

8. 自定义分页

8.1 分页
制作测试环境.
	# tests.py 
    # 将大于主键>100的都删除
    models.Book1.objects.filter(pk__gt=100).delete()
分析:
数据全部展示在一页是不实际的,将数据分页展示.
1. 每页展示多少条 

QuerySet对象 是一个列表套字典的格式, 支持切片操作
每页展示10条数据

per_page_num = 10    一页十条
	页码               起始          终止
current_page        start_page     end_page
	1				 0		       10       展示的数据 0 - 9
	2				 10			   20       展示的数据 10 - 19
	3				 20		       30       展示的数据 20 - 29
2. 获取访问的页码

使用get请求携带访问页码  127.0.0.1:8000/?page=1
current_page = request.GET.get('page', 1)    如果获取不到页面的参数就展示第一页

* url?后面的数据是字符串
3. 计算公式
	
当前页面 current_page = reuqest.GET.get('page', 1)
每页数 per_page_num = 10
start_page =  (page_id - 1) * per_page_num
end_page  =  current_page * per_page_num-->10
4. 取数据

book_queryset = models.Book.objects.all()[start_page:end_page]
	# urls.py
    # 11. 分页器
    url('^pager/', views.pager),
# 10. 分页
def pager(request):
    # 10.1 get 请求的获取页码 获取不到page的参数就设置为1
    page_id = request.GET.get('page', 1)
    print(page_id, type(page_id))
    # 10.2 判断 page_id 是不是纯数字
    if page_id.isdigit():
        # 是就转换为整型
        page_id = int(page_id)
    else:
        # 不是存数据就说明输入是有问题的, 就直接把参数设置为1
        page_id = 1

    # 10.3 自定义分页器每页展示的数据
    per_page_num = 10

    # 10.4 分页公式
    start_page = (page_id - 1) * per_page_num
    end_page = page_id * per_page_num

    # 10. 5 获取数据并按索引切片              如果是第一页   0          10   获取 0-9
    queryset_obj = models.Book1.objects.all()[start_page: end_page]

    # 10.6 返回页面个 数据
    return render(request, 'pager.html', locals())

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>分页器title>
    
    {% load static %}
    
    <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
    
    <script src="{% static 'dist/sweetalert.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
head>
<body>
<div>
    
    {% for foo in queryset_obj %}
        <p>{{ foo.pk }} {{ foo.title }}p>
    {% endfor %}

div>
body>
html>
http://127.0.0.1:8000/pager/?page=1   访问第一页
http://127.0.0.1:8000/pager/?page=2   访问第一页

7.Django之Ajax与分页器_第22张图片

8.2 计算总页码
总数据 99  每页10 需要 10
总数据 100  每页10 需要 10
总数据 101  每页10 需要 11

通过divmod() 函数 动态的计算出多少页,

divmod(100, 10)
 余数
(10, 0)

divmod(101, 10)
(10, 1)

divmod(99, 10)
(9, 9)

余数不为0加一页
# 10. 分页
def pager(request):
    # 10.1 get 请求的获取页码 获取不到page的参数就设置为1
    page_id = request.GET.get('page', 1)
    print(page_id, type(page_id))
    # 10.2 判断 page_id 是不是纯数字
    if page_id.isdigit():
        # 是就转换为整型
        page_id = int(page_id)
    else:
        # 不是存数据就说明输入是有问题的, 就直接把参数设置为1
        page_id = 1

    # 10.3 读取数据库中所有的数据
    all_queryset_obj = models.Book1.objects.all()

    # 10.4 自定义分页器每页展示的数据
    per_page_num = 10

    # 10.5 计算页码
    page_num, residue = divmod(all_queryset_obj.count(), per_page_num)

    # 10.6 判断余数 只是residue为0 才不执行
    if residue:
        page_num += 1

    # 10.7 判断输入的页码id是否存在, 不存在就访问第一页
    page_num_tuple = range(page_num + 1)
    if page_id not in page_num_tuple:
        page_id = 1

    # 10.8 分页公式
    start_page = (page_id - 1) * per_page_num
    end_page = page_id * per_page_num

    # 10. 9 获取数据并按索引切片              如果是第一页   0          10   获取 0-9
    queryset_obj = models.Book1.objects.all()[start_page: end_page]

    # 10.10 返回页面个 数据
    return render(request, 'pager.html', locals())

7.Django之Ajax与分页器_第23张图片

8.3 页码器
在制作页面的时候一般都是奇数个符合对称审美.

7.Django之Ajax与分页器_第24张图片


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>分页器title>
    
    {% load static %}
    
    <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
    
    <script src="{% static 'dist/sweetalert.js' %}">script>
    
    <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
head>
<body>
<div>
    
    {% for foo in queryset_obj %}
        <p>{{ foo.pk }} {{ foo.title }}p>
    {% endfor %}
 	   
    <nav aria-label="Page navigation">
  <ul class="pagination">
    <li>
      <a href="#" aria-label="Previous">
        <span aria-hidden="true">«span>
      a>
    li>
    <li><a href="?page=1">1a>li>
    <li><a href="?page=2">2a>li>
    <li><a href="?page=3">3a>li>
    <li><a href="?page=4">4a>li>
    <li><a href="?page=5">5a>li>
    <li>
      <a href="#" aria-label="Next">
        <span aria-hidden="true">»span>
      a>
    li>
  ul>
nav>
div>
body>
html>

7.Django之Ajax与分页器_第25张图片

8.4 分页器计算
动态产生: <li><a href="?page=1">1</a></li>
统计总页数,产生对应的a标签.
在后端写html代码.
后端:
h1 = '

xxx

'
|safe 前端转义: <p>{{ h1|safe }}</p>
# 10. 分页
def pager(request):
    # 10.1 get 请求的获取页码 获取不到page的参数就设置为1
    page_id = request.GET.get('page', 1)
    print(page_id, type(page_id))
    # 10.2 判断 page_id 是不是纯数字
    if page_id.isdigit():
        # 是就转换为整型
        page_id = int(page_id)
    else:
        # 不是存数据就说明输入是有问题的, 就直接把参数设置为1
        page_id = 1

    # 10.3 读取数据库中所有的数据
    all_queryset_obj = models.Book1.objects.all()

    # 10.4 自定义分页器每页展示的数据
    per_page_num = 10

    # 10.5 计算页码
    page_num, residue = divmod(all_queryset_obj.count(), per_page_num)

    # 10.6 判断余数 只是residue为0 才不执行
    if residue:
        page_num += 1

    # 10.7 判断输入的页码id是否存在, 不存在就访问第一页
    page_num_tuple = range(page_num + 1)
    if page_id not in page_num_tuple:
        page_id = 1

    # 10.8 html代码
    pager_html = ''
    for i in range(1, page_num + 1):
        pager_html += f'
  • {i}
  • '
    print(pager_html) # 10.9分页公式 start_page = (page_id - 1) * per_page_num end_page = page_id * per_page_num # 10.10 获取数据并按索引切片 如果是第一页 0 10 获取 0-9 queryset_obj = models.Book1.objects.all()[start_page: end_page] # 10.11 返回页面个 数据 return render(request, 'pager.html', locals())
    
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>分页器title>
        
        {% load static %}
        
        <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
        
        <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
        
        <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
        
        <script src="{% static 'dist/sweetalert.js' %}">script>
        
        <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
    head>
    <body>
    <div>
        
        {% for foo in queryset_obj %}
            <p>{{ foo.pk }} {{ foo.title }}p>
        {% endfor %}
        <nav aria-label="Page navigation">
      <ul class="pagination">
        <li>
          <a href="#" aria-label="Previous">
            <span aria-hidden="true">«span>
          a>
        li>
            {{ pager_html|safe }}
        <li>
          <a href="#" aria-label="Next">
            <span aria-hidden="true">»span>
          a>
        li>
      ul>
    nav>
    div>
    body>
    html>
    

    7.Django之Ajax与分页器_第26张图片

    7.Django之Ajax与分页器_第27张图片

    展示分页器全部的按钮.
    
    8.5 分页器规划
        pager_html = ''
        for i in range(1, page_num + 1):
            pager_html += f'
  • {i}
  • '
    print(pager_html) # 这个代码将所有的按钮都现在出来了
    设置只展示5, (一般都显示的都为奇数的)
    							
                                  url?page=3
    page_id - 2    page_id - 1     page_id   page_id + 1  page_id + 2
     	1				2  			3         4             5
     							高亮当前行 为li标签 设置 class='active'
    range(page_id-2, page_id+3)
    
        # 10.8 html代码
        pager_html = ''
        for i in range(page_id - 2, page_id + 3):
            if i == page_id:
                pager_html += f'
  • {i}
  • '
    else: pager_html += f'
  • {i}
  • '

    7.Django之Ajax与分页器_第28张图片

    左右两边不能超出范围,page_id为 当前页码,page_num总页数.
    左边不能为0 右边最大为当前的总页数.
    
    	# 定位点数字
        position_num =  page_id
        # 左不能为0   page_id为 1 2 3 的时候固定展示  1 2 3 4 5
        if page_id <= 3:
            position_num = 3
        # 右边不能超过  page_num总页数   如果总页数为8, 9, 10   固定展示 6 7 8 9 10
        if page_id + 3 >= page_num:
            position_num = page_num - 2
    
    # 10. 分页
    def pager(request):
        # 10.1 get 请求的获取页码 获取不到page的参数就设置为1
        page_id = request.GET.get('page', 1)
        print(page_id, type(page_id))
        # 10.2 判断 page_id 是不是纯数字
        if page_id.isdigit():
            # 是就转换为整型
            page_id = int(page_id)
        else:
            # 不是存数据就说明输入是有问题的, 就直接把参数设置为1
            page_id = 1
    
        # 10.3 读取数据库中所有的数据
        all_queryset_obj = models.Book1.objects.all()
    
        # 10.4 自定义分页器每页展示的数据
        per_page_num = 10
    
        # 10.5 计算页码 page_num页码  residue余数
        page_num, residue = divmod(all_queryset_obj.count(), per_page_num)
    
        # 10.6 判断余数 只是residue为0 才不执行
        if residue:
            page_num += 1
    
        # 10.7 判断输入的页码id是否存在, 不存在就访问第一页
        page_num_tuple = range(page_num + 1)
        if page_id not in page_num_tuple:
            page_id = 1
    
        # 10.8 html代码
        pager_html = ''
        position_num = page_id
        # 左不能为0
        if page_id <= 3:
            position_num = 3
        # 右边不能超过  page_num
        if page_id + 3 >= page_num:
            position_num = page_num - 2
        for i in range(position_num - 2, position_num + 3):
            if i == page_id:
                pager_html += f'
  • {i}
  • '
    else: pager_html += f'
  • {i}
  • '
    print(pager_html) # 10.9分页公式 start_page = (page_id - 1) * per_page_num end_page = page_id * per_page_num # 10.10 获取数据并按索引切片 如果是第一页 0 10 获取 0-9 queryset_obj = models.Book1.objects.all()[start_page: end_page] # 10.11 返回页面个 数据 return render(request, 'pager.html', locals())

    9. 分页模板

    在使用非django内置的第三方功能或者组件的时候我们一帮情况下个会创建一个
    utils文件夹,在该文件夹内对模块进行功能的划分.
    utils文件夹也在每个单独的app下创建.
    在后期封装代码的时候不在局限与函数,尽量使用面向对象去封装.
    将下列代码复制到utils文件夹内的一个自创的文件中.
    
    基于bootstrap样式,需要提前导入.
    
    9.1 模板
    class Pagination(object):
        def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
            """
            封装分页相关数据
            :param current_page: 当前页
            :param all_count:    数据库中的数据总条数
            :param per_page_num: 每页显示的数据条数
            :param pager_count:  最多显示的页码个数
            """
            try:
                current_page = int(current_page)
            except Exception as e:
                current_page = 1
    
            if current_page < 1:
                current_page = 1
    
            self.current_page = current_page
    
            self.all_count = all_count
            self.per_page_num = per_page_num
    
            # 总页码
            all_pager, tmp = divmod(all_count, per_page_num)
            if tmp:
                all_pager += 1
            self.all_pager = all_pager
    
            self.pager_count = pager_count
            self.pager_count_half = int((pager_count - 1) / 2)
    
        @property
        def start(self):
            return (self.current_page - 1) * self.per_page_num
    
        @property
        def end(self):
            return self.current_page * self.per_page_num
    
        def page_html(self):
            # 如果总页码 < 11个:
            if self.all_pager <= self.pager_count:
                pager_start = 1
                pager_end = self.all_pager + 1
            # 总页码  > 11
            else:
                # 当前页如果<=页面上最多显示11/2个页码
                if self.current_page <= self.pager_count_half:
                    pager_start = 1
                    pager_end = self.pager_count + 1
    
                # 当前页大于5
                else:
                    # 页码翻到最后
                    if (self.current_page + self.pager_count_half) > self.all_pager:
                        pager_end = self.all_pager + 1
                        pager_start = self.all_pager - self.pager_count + 1
                    else:
                        pager_start = self.current_page - self.pager_count_half
                        pager_end = self.current_page + self.pager_count_half + 1
    
            page_html_list = []
            # 添加前面的nav和ul标签
            page_html_list.append('''
                        
                                               
                                           ''')
            return ''.join(page_html_list)
    
    9.2 使用模板
    	# 12. 分页器模板
        url('^pager_model/', views.pager_model)
    
    # 11. 分页器模板
    def pager_model(request):
        # 11.1 导入分页器模板模块
        from app01.utils.page import Pagination
        # 11.2 获取所有数据
        all_queryset_obj = models.Book1.objects.all()
        # 11.3 计算总数
        all_queryset_sum = all_queryset_obj.count()
    
        # 11.4 获取当前的页码  获取的是字符串
        page_num = request.GET.get('page_id', 1)
        # 11.5 实例化对象
        page_obj = Pagination(
            current_page=page_num,  # 自动转换类型
            all_count=all_queryset_sum,
            per_page_num=10,
            pager_count=5,
    
        )
        """ 
        current_page: 当前页               必须的 自动转换类型
        all_count:    数据库中的数据总条数  必须的 
        per_page_num: 每页显示的数据条数    默认2
        pager_count:  最多显示的页码个数    默认10
        """
        # 11.6 切片操作
        page_queryset = all_queryset_obj[page_obj.start: page_obj.end]
        # 11.7 返回页面 与 页面数据
        return render(request, 'pager_model.html', locals())
    
    
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>分页器模板title>
        
        {% load static %}
        
        <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
        
        <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
        
        <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>
        
        <script src="{% static 'dist/sweetalert.js' %}">script>
        
        <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
    head>
    <body>
    <div>
        
        {% for page in page_queryset %}
            <p>{{ page.pk }} {{ page.title }}p>
            <nav aria-label="Page navigation">nav>
        {% endfor %}
        {# 利用自动分页器 #}
        {{ page_obj.page_html|safe }}
    div>
    body>
    html>
    

    7.Django之Ajax与分页器_第29张图片

    你可能感兴趣的:(3.Django,ajax,django,前端)