Ajax进阶

前后端传输数据的编码格式(contentType)

python 复制代码
# 提示: 主要研究post请求数据的编码格式.
'''
get请求数据就是直接放在url?号后面的每个参数之间用&符连接, 如下:
    url?username=jason&password=123
'''

# 可以朝后端发送post请求的方式
    1 .form表单
    2. ajax请求

# 基于post请求. 前后端传输数据的3种编码格式
    urlencoded
    form-data
    json


# 基于post请求. form表单传输数据的编码格式
    默认:  默认的数据编码格式是urlencoded
    数据格式: username=jason&password=123
    结论: django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中. 如下所示:
        username=jason&password=123	>>> request.POST
    !!!注意!!!: 我们这里指的是符合. 如果你指定的是form-data编码格式, 针对普通的键值对还是解析到request.POST中, 而文件将会解析到request.FILES中

# 基于post请求. ajax传输数据的编码格式
    默认:  默认的数据编码格式是urlencoded
    数据格式: username=jason&password=123
    结论: django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中. 如下所示:
        username=jason&age=20	>>> request.POST

Ajax发送json格式数据

python 复制代码
# 强调: 前后端传输数据的时候一定要确保编码格式跟数据真正的格式是一致的. 比如: 你的编码格式指定的是json格式. 那么你的data数据必须也要是json格式.
# request对象方法补充: request.is_ajax()  判断当前请求是否是ajax请求 返回布尔值
# 前后提交json格式数据给后端流程:
'''
1. 前端页面ajax中指定参数(注意: 都是基于post请求方式提交数据)
    1.1 指定数据编码格式: contentType: application/json
    1.2 对发送给后端的数据进行序列化: data: JSON.stringify({'username': 'egon', 'age': 18})
2. 后端
    2.1 json格式的数据django后端不会不会做任何的处理. 需要我们手动 通过request.body获取二进制格式的json数据
    2.2 后端有2种方式将这种格式反解成python数据类型
        第一种: 先使用decode解码成str类型. 再使用json.loads反序列化成python数据类型
'''

# 前端
$('#d1').click(function () {
    $.ajax({
        url:'',
        type:'post',
        {#data:{'username':'jason','age':25},#}
        data:JSON.stringify({'username':'jason','age':25}),
        contentType:'application/json',  // 指定编码格式
        success:function () {

        }
    })
})


# 后端
def ab_json(request):
if request.is_ajax():
    print(request.is_ajax())  # True
    print(request.POST)       # <QueryDict: {}>
    print(request.FILES)      # <MultiValueDict: {}>
    print(request.body)       # b'{"username":"jason","age":25}'
    # 手动处理json格式数据: 先反序列化, 再解码
    '''
    json_bytes = request.body
    json_str = json_bytes.decode('utf-8')
    json_dict = json.loads(json_str)
    print(json_dict, type(json_dict))  # {'username': 'jason', 'age': 25} <class 'dict'>
    '''

    # 由loads内部自动处理: json.loads括号内如果传入了一个二进制格式的数据那么内部自动解码再反序列化
    '''
    json_bytes = request.body
    json_dict = json.loads(json_bytes)  # {'username': 'jason', 'age': 25} <class 'dict'>
    print(json_dict, type(json_dict))  # {'username': 'jason', 'age': 25} <class 'dict'>
    '''
return render(request, 'ab_json.html')

Ajax发送文件

python 复制代码
# 提示: ajax发送文件需要借助于js内置对象FormData
# 前端发送文件对象步骤:
'''
1. new一个FormData对象
    let formDataObj = new FormData()
2. 添加数据的2种方式
    第一种: 添加普通键值对
        formDataObj.append(key, value)
    第二种: 添加文件对象
        formDataObj.append(key, $('#file')[0].files[0])
3. ajax中参数指定
    data: formDataObj,  // 指定发送的数据. 直接放对象
    contentType: false, // 编码类型不指定. django后端能够自动识别form-data对象
    格式发送
    processData: false, // 指定处理数据为false. 让浏览器不要格外的处理data中的数据.
'''
# 后端接收文件对象步骤
'''
提示: django后端能够直接识别到form-data对象并且能够将内部的普通键值自动解析并封装到request.POST中 文件数据自动解析并封装到request.FILES中
1. 使用request.is_ajax()判断是否是一个ajax请求
2. 使用request.method判断是否是POST请求
3. 使用request.FIlES获取到文件对象
'''

# 前端
// 点击按钮朝后端发送普通键值对和文件数据
$('#d4').on('click',function () {
    // 1 需要先利用FormData内置对象
    let formDateObj = new FormData();
    // 2 添加普通的键值对
    formDateObj.append('username',$('#d1').val());
    formDateObj.append('password',$('#d2').val());
    // 3 添加文件对象
    formDateObj.append('myfile',$('#d3')[0].files[0]);
    // 4 将对象基于ajax发送给后端
    $.ajax({
        url:'',
        type:'post',
        data:formDateObj,  // 直接将对象放在data后面即可

        // ajax发送文件必须要指定的两个参数
        contentType:false,  // 不需使用任何编码 django后端能够自动识别form-data对象
        processData:false,  // 告诉你的浏览器不要对你的数据进行任何处理

        success:function (args) {
        }
    })
})

# 后端
def ab_file(request):
    if request.is_ajax():
        if request.method == 'POST':
            print(request.POST)
            print(request.FILES)
    return render(request, 'ab_file.html')

Django自带的序列化组件

python 复制代码
# 前提: 前后端分离. django中无法直接使用模板语法
# 使用: 使用django自带的序列化方法serializers自动帮我们序列化
'''
from django.core import serializers
user_queryset = models.User.objects.all()
res = serializers.serialize('json', user_queryset)
    第一个参数指定需要序列化的类型
    第二个参数指定需要序列化的数据
    返回结果: 返回一个列表套字典的序列化以后的数据格式.
'''
# 注意: 使用serializers自动帮我么序列化的数据格式中, 指定choices参数得字段gender拿到的是存储的格式. 没有自动帮我们'对象.get_gender_display()'处理展示, 因此需要写一个接口文档 交代交代


def ab_ser(request):
# 1. 当前后端不分离的清空下后端的数据提交给前端通过模板语法即可
'''
user_queryset = models.User.objects.all()
return render(request, 'ab_ser.html', locals())
'''

# 2. 当前后端分离的情况下: 通过手动序列化数据格式将序列化好的接口提交给前端即可
'''
user_queryset = models.User.objects.all()
# [{},{},{},{},{}]
user_list = []
for user_obj in user_queryset:
    tmp = {
        'pk': user_obj.pk,
        'username': user_obj.username,
        'age': user_obj.age,
        'gender': user_obj.get_gender_display()
    }
    user_list.append(tmp)
from django.http import JsonResponse
return JsonResponse(user_list, safe=False)
'''

# 3. 当前后端分离的情况下: 通过django自带的自动序列化数据格式的方法serializers实现
from django.core import serializers
user_queryset = models.User.objects.all()
res = serializers.serialize('json', user_queryset)
"""会自动帮你将数据变成json格式的字符串 并且内部非常的全面"""
return HttpResponse(res)

# 手动序列化数据格式
"""
[
 {"pk": 1, "username": "jason", "age": 25, "gender": "male"},
 {"pk": 2, "username": "egon", "age": 31, "gender": "female"},
 {"pk": 3, "username": "kevin", "age": 32, "gender": "others"},
 {"pk": 4, "username": "tank", "age": 40, "gender": 4}
 ]
"""


# 利用serializers自动序列化数据格式
# 注意: 使用serializers自动帮我么序列化的数据格式中, 指定choices参数得字段gender拿到的是存储的格式. 没有自动帮我们'对象.get_gender_display()'处理展示, 因此需要写一个接口文档 交代交代
"""
[
{   "model": "app01.user",
    "pk": 1,
    "fields": {"username": "jason", "age": 25, "gender": 1}},

{   "model": "app01.user",
    "pk": 2,
    "fields": {"username": "egon", "age": 31, "gender": 2}},

{   "model": "app01.user",
    "pk": 3,
    "fields": {"username": "kevin", "age": 32, "gender": 3}},

{   "model": "app01.user",
    "pk": 4,
    "fields": {"username": "tank", "age": 40, "gender": 4}}
]
"""

Ajax结合sweetAlert

python 复制代码
# 步骤流程:
'''
# 前端
1. 第一步: 删除按钮使用button而不是用a标签. 这里因为是ajax含有url, 使用a标签的href会冲突. 然后去指定要声明删除的数据行的的主键del_id, 是为了后端filter可以定位到这条记录. 然后可以删除
    <button class="btn btn-xs btn-danger del_btn" del_id="{{ user_obj.pk }}">删除</button>
2. 第二步: 使用JQ查找定位绑定点击事件将会触发swal (提示: 使用sweetAlert可以下载下来以后配置static静态. 也可以使用bootCdn)
3. 在isConfirm中使用ajax
4. 二种方式传递主键:
    第一种: 在url中  my_user_list/'+currentBtn.attr('del_id')
    第二种: 在data中 JSON.stringify({'del_id': currentBtn.attr('del_id')}

# 后端
1. is_ajax + method判断处理请求
2. 定制返回客户端的字段数据格式stat_dic. 里面包含自定义的状态码. 以及信息等
3. 如果需要配合sweetAlert中的'showLoaderOnConfirm: true'一起使用. 后端就通过import time模拟网络延迟


# 前端
1. 通过后端return的参数. ajax回调函数中agrs参数拿到. 判断状态码. 不同的状态码做不同的逻辑处理
2. 删除成功以后毕动态刷新.  2种方式
    方式一: 直接刷新当前页面     window.location.reload()
    方式二: 利用DOM操作 动态刷新 currentBtn.parent().parent().remove();
'''

# 前端
<style>
    div.sweet-alert h2 {
        padding-top: 10px;  /*处理swal输入中文出现展示补全的现象 --> swal("删除完毕", `你目前把${args.msg}删除了`, "success");*/
    }
</style>
<script>
    $(".del_btn").on('click', function () {
        {#alert($(this).attr('del_id'));#}
        // 先将当前标签对象存储起来. 如果不存储起来. 下面在回调函数success中使用this代指的就是回调函数这个对象. 而不是我们执行点击事件的对象.
        let currentBtn = $(this);

        swal({
                title: "你正在执行删除操作",
                text: "你确定要删除吗?",
                type: "warning",
                showCancelButton: true,
                confirmButtonClass: "btn-danger",
                confirmButtonText: "确定",
                cancelButtonText: "取消",
                closeOnConfirm: false,
                closeOnCancel: false,
                showLoaderOnConfirm: true,
            },
            function (isConfirm) {
                if (isConfirm) {
                    $.ajax({
                        {#url: 'my_user_list/'+currentBtn.attr('del_id'),#}  // 1. 传递主键值方式一: 放在url中. (注意: 传递到后端是str类型需要转)
                        url: '',
                        type: 'post',
                        data: JSON.stringify({'del_id': currentBtn.attr('del_id')}),  // 2. 传递主键值方式二: 放在url中. 我们这里使用json格式传输数据. 方便数据的交互. 省略了后端拿主键值只拿到str类型时需要转换. 以及后端传输int类型的状态码等等. (提示: 这里的状态码可以是字符串格式的定义传输过来)
                        contentType: 'application/json',
                        success: function (args) {
                            // 判断响应状态码 然后做不同的处理  {'stat_code': 1001, 'msg': del_username}
                            if (args.stat_code === 1000) {
                                swal("删除完毕", `你目前把${args.msg}删除了`, "success");
                                // 1. 删除数据完毕动态刷新方式一: lowb版本 直接刷新当前页面
                                // window.location.reload()
                                // 2. 删除数据完毕动态刷新方式二: 利用DOM操作 动态刷新
                                currentBtn.parent().parent().remove();
                                {#console.log(this);#}
                                {#console.log(currentBtn);#}
                            } else {
                                swal("删除错误", `删除${args.msg}出现不可抗力的力量!`, "warning");
                            }
                        }
                    });

                } else {
                    swal("再见啊朋友", "下此再来吧!", "error");
                }
            });
    })
</script>


# 后端
import json
from django.http import JsonResponse

def my_user_list(request):
    if request.is_ajax():
        if request.method == 'POST':

            del_id = json.loads(request.body).get('del_id')
            if del_id:
                del_queryset = models.User.objects.filter(pk=del_id)
                del_username = del_queryset.first().username
                # stat = {'stat_code': 1001, 'msg': del_username}

                # del_queryset.delete()
                import time
                time.sleep(1)  # 模拟操作数据的延迟
                stat = {'stat_code': 1000, 'msg': del_username}
                return JsonResponse(stat, json_dumps_params={'ensure_ascii': False})

    user_queryset_obj = models.User.objects.all()
    return render(request, 'my_user_list.html', locals())

批量插入

python 复制代码
'''
提示: 当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间.
'''

import time
def ab_pl(request):
# 普通插入create: 先给Book插入一千条数据. 再将所有的数据查询并展示到前端页面
'''
start_time = time.time()

for i in range(1000):
    models.Book.objects.create(title='第%s本书' % i)
book_queryset = models.Book.objects.all()

stop_time = time.time()
print(stop_time-start_time)  # 90.015675783157349
return render(request, 'ab_pl.html', locals())
'''

# 批量插入bulk_create: 当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间
start_time = time.time()

'''
book_list = []
for i in range(1000):
    book_obj = models.Book(title='第%s本书' % i)   // 注意: ORM查询语句的惰性原则, 没有执行就不会走数据库
    book_list.append(book_obj)
models.Book.objects.bulk_create(book_list)
'''
# 使用列表不太合理我们下面使用生成器
book_generator = (models.Book(title='第%s本书' % i) for i in range(1000))
models.Book.objects.bulk_create(book_generator)
book_queryset = models.Book.objects.all()

stop_time = time.time()
print(stop_time-start_time)   # 0.14934825897216797
return render(request, 'ab_pl.html', locals())

分页器

1. 分页器思路

python 复制代码
"""
总数据100 每页展示10 需要10
总数据101 每页展示10 需要11
总数据99 每页展示10  需要10

如何通过代码动态的计算出到底需要多少页?


在制作页码个数的时候 一般情况下都是奇数个		符合中国人对称美的标准
"""
# 分页
    book_list = models.Book.objects.all()

    # 想访问哪一页
    current_page = request.GET.get('page',1)  # 如果获取不到当前页码 就展示第一页
    # 数据类型转换
    try:
        current_page = int(current_page)
    except Exception:
        current_page = 1
    # 每页展示多少条
    per_page_num = 10
    # 起始位置
    start_page = (current_page - 1) * per_page_num
    # 终止位置
    end_page = current_page * per_page_num

    # 计算出到底需要多少页
    all_count = book_list.count()

    page_count, more = divmod(all_count, per_page_num)
    if more:
        page_count += 1

    page_html = ''
    xxx = current_page
    if current_page < 6:
        current_page = 6
    for i in range(current_page-5,current_page+6):
        if xxx == i:
            page_html += '<li class="active"><a href="?page=%s">%s</a></li>'%(i,i)
        else:
            page_html += '<li><a href="?page=%s">%s</a></li>'%(i,i)



    book_queryset =  book_list[start_page:end_page]
    
"""
django中有自带的分页器模块 但是书写起来很麻烦并且功能太简单
所以我们自己想法和设法的写自定义分页器

我们基于上述的思路 已经封装好了我们自己的自定义分页器 
之后需要使用直接拷贝即可
"""

2. 自定义分页器的拷贝及使用

2.1 第一步: 新建utils文件夹. 自定义.py文件. 我们这里以: pager.py

当我们需要使用到非django内置的第三方功能或者组件代码的时候, 我们一般情况下会创建一个名为utils文件夹 在该文件夹内对模块进行功能性划分, 而utils这个文件夹可以在每个应用下创建 具体结合实际情况

python 复制代码
class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=10, pager_count=7):
        """
        封装分页相关数据
        :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('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
            else:
                temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)
2.2 第二步: 视图层views.py中使用
python 复制代码
from utils.pager import Pagination

current_page = request.GET.get('page', 1)
book_queryset = models.Book.objects.all()
all_count = book_queryset.count()

# 1. 传值生成对象
page_obj = Pagination(current_page=current_page, all_count=all_count)
# 2. 直接对总数据进行切片操作
page_queryset = book_queryset[page_obj.start:page_obj.end]
# 3. 将page_queryset传递到页面. 这就是当前所展示的页面部分
return render(request, 'ab_pl.html', locals())
2.3 第三步: 模板层中使用
python 复制代码
'''
我们自定义的分页器是基于bootstrap样式来的 所以你需要提前导入bootstrap. 而bootstrap的js部分内部通过jq封装的, 我们先导入jq.
导入流程:
    jQuery.min.js  --> bootstrap.min.js
版本要求:
    bootstrap 版本 v3
    jQuery    版本 v3
'''
python 复制代码
{% for book_obj in page_queryset %}
    <p>{{ book_obj.title }}</p>    {# 分页的内容部分 #}
{% endfor %}

{#利用自定义分页器直接显示分页器样式#}
{{ page_obj.page_html|safe }}
相关推荐
叠层归一研究院8 分钟前
如何用程序搭建一个 AGI 种子系统(一):从向量种子到无限生长引擎
人工智能·python·算法·机器学习·agi
水深火乐16 分钟前
安全地生成验证码、密码和 Token
后端
小强198828 分钟前
SQL Server 慢查询怎么定位?一套从“卡”到“快”的完整排查流程
后端
水深火乐30 分钟前
interface和any
后端
步行cgn31 分钟前
MyBatis Error evaluating expression ‘ids‘. Return value (3) was not iterable 错误详
java·后端
yujunl34 分钟前
总结一下U9客开的成果
开发语言
神奇小汤圆34 分钟前
Spring Boot + LangChain4j实现RAG——从零搭建企业级知识库问答系统
后端
独隅34 分钟前
KMP 全栈进化:Koog 框架打造纯 Kotlin AI Agent 实战效果
开发语言·人工智能·kotlin
H_Peak39 分钟前
Python运算符与流程控制:if条件判断
后端
H_Peak41 分钟前
Python字符串操作:从基础到正则入门
后端