前后端传输数据的编码格式(contentType)
# 提示: 主要研究post请求数据的编码格式.
'''
get请求数据就是直接放在url?号后面的每个参数之间用&符连接, 如下:url?username=jason&password=123
'''# 可以朝后端发送post请求的方式1 .form表单2. ajax请求# 基于post请求. 前后端传输数据的3种编码格式urlencodedform-datajson# 基于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格式数据
# 强调: 前后端传输数据的时候一定要确保编码格式跟数据真正的格式是一致的. 比如: 你的编码格式指定的是json格式. 那么你的data数据必须也要是json格式.
# request对象方法补充: request.is_ajax() 判断当前请求是否是ajax请求 返回布尔值
# 前后提交json格式数据给后端流程:
'''
1. 前端页面ajax中指定参数(注意: 都是基于post请求方式提交数据)1.1 指定数据编码格式: contentType: application/json1.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()) # Trueprint(request.POST) # <QueryDict: {}>print(request.FILES) # <MultiValueDict: {}>print(request.body) # b'{"username":"jason","age":25}'# 手动处理json格式数据: 先反序列化, 再解码'''json_bytes = request.bodyjson_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.bodyjson_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发送文件
# 提示: 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自带的序列化组件
# 前提: 前后端分离. 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
# 步骤流程:
'''
# 前端
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 JsonResponsedef 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 timetime.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())
批量插入
'''
提示: 当你想要批量插入数据的时候 使用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. 分页器思路
"""
总数据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 += 1page_html = ''xxx = current_pageif current_page < 6:current_page = 6for 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这个文件夹可以在每个应用下创建 具体结合实际情况
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 = 1if current_page < 1:current_page = 1self.current_page = current_pageself.all_count = all_countself.per_page_num = per_page_num# 总页码all_pager, tmp = divmod(all_count, per_page_num)if tmp:all_pager += 1self.all_pager = all_pagerself.pager_count = pager_countself.pager_count_half = int((pager_count - 1) / 2)@propertydef start(self):return (self.current_page - 1) * self.per_page_num@propertydef end(self):return self.current_page * self.per_page_numdef page_html(self):# 如果总页码 < 11个:if self.all_pager <= self.pager_count:pager_start = 1pager_end = self.all_pager + 1# 总页码 > 11else:# 当前页如果<=页面上最多显示11/2个页码if self.current_page <= self.pager_count_half:pager_start = 1pager_end = self.pager_count + 1# 当前页大于5else:# 页码翻到最后if (self.current_page + self.pager_count_half) > self.all_pager:pager_end = self.all_pager + 1pager_start = self.all_pager - self.pager_count + 1else:pager_start = self.current_page - self.pager_count_halfpager_end = self.current_page + self.pager_count_half + 1page_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中使用
from utils.pager import Paginationcurrent_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 第三步: 模板层中使用
'''
我们自定义的分页器是基于bootstrap样式来的 所以你需要提前导入bootstrap. 而bootstrap的js部分内部通过jq封装的, 我们先导入jq.
导入流程:jQuery.min.js --> bootstrap.min.js
版本要求:bootstrap 版本 v3jQuery 版本 v3
'''
{% for book_obj in page_queryset %}<p>{{ book_obj.title }}</p> {# 分页的内容部分 #}
{% endfor %}{#利用自定义分页器直接显示分页器样式#}
{{ page_obj.page_html|safe }}