一、多对多的三种创建方式
1、三种方式
1、全自动(较为常用)
2、纯手动(了解)
3、半自动(推荐使用)
2、优缺点比较
1、全自动:
优点:第三张表自动帮你创建
缺点:第三张表无法扩展额外的字段
2、纯手动:
优点:第三张表可以扩展额外的字段
缺点:orm查询的时候会带来不便
3、半自动:
优点:第三张表可以扩展任意的额外字段 还可以利用orm 的正反向查询
缺点:无法利用 add set remove clear方法,但是虽然你无法使用 你还可以自己直接操作第三张表,所以推荐使用半自动
3、简易代码演示
# 全自动 class Book(models.Model): title = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author') # 第三方表orm帮我们创建 class Author(models.Model): name = models.CharField(max_length=32) # 纯手动 class Book(models.Model): title = models.ManyToManyField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): book = models.ForeignKey(to='Book') # 书的外键字段,注意此时后面不用加_id因为django的orm会自动添加 author = models.ForeignKey(to='Author') # 作者表的外键字段,注意此时后面不用加_id因为django的orm会自动添加 create_time = models.DateField(auto_now_add=True) # 半自动 class Book(models.Model): title = models.ManyToManyField(max_length=32) authors = models.ManyToManyField(to='Author', through='Book2Author', through_fields=('book', 'author')) # 注意:多对多外键字段创在哪一方都可以,上面的这句话的意思是,告诉orm第三张表你不用帮我创了,我已经创好了through='Book2Author',告诉他我的第三张表是哪一个, # through_fields=('book', 'author')是告诉orm我的哪两个字段是外键字段,然后你去给我绑定关系即可,因为字段可能有多个 class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=('author', 'book')) # 这是外键字段绑在作者表上的写法,
# 总结:在那张下面绑定,through_fields=()中第一个参数就写那张表 class Book2Author(models.Model): book = models.ForeignKey(to='Book') # 书的外键字段,注意此时后面不用加_id因为django的orm会自动添加 author = models.ForeignKey(to='Author') # 作者表的外键字段,注意此时后面不用加_id因为django的orm会自动添加 create_time = models.DateField(auto_now_add=True)
二、Ajax
1、Ajax特点简单介绍(八个字)
异步提交
同步:任务提交之后原地等待任务返回的结果 从进程表现上来说 就好像是阻塞
异步:任务提交之后不需要原地等地等待返回结果 直接执行下一行代码 从进程表现上来说 非阻塞
局部刷新
2、Ajax简介
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
- 整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
- 当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!
3、Ajax常见应用场景
搜索引擎根据用户输入的关键字,自动提示检索关键字。
还有一个很重要的应用场景就是注册时候的用户名的查重。
其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。
- 整个过程中页面没有刷新,只是局部刷新了;
- 在请求发出后,浏览器不用等待服务器响应结果就可以进行其他操作;
4、Ajax优缺点
优点:
- AJAX使用JavaScript技术向服务器发送异步请求;
- AJAX请求无须刷新整个页面;
- 因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;
- 两个关键点:1.局部刷新,2.异步请求
5、与后端进行交互的方式
1、浏览器窗口输入url 然后回车 GET
2、a标签href属性填写url 点击 GET
3、form表单 GET/POST
4、Ajax GET/POST
注意: Ajax并不是一门语言 它其实就是基于js写的一个功能模块而已
由于原生的js书写Ajax较为繁琐,所以这里我们直接学jQuery封装好的Ajax模块操作
三、初始Ajax以及Ajax的基本语法结构
1、一个小案列初始Ajax
需求:页面上有三个input框 一个按钮 用户在前面两个框中输入数字 点击按钮保证页面不刷新的情况下,
将数据发送给后端做计算,再将计算好的结果发送给前端展示到第三个input框中。
"""day55 URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), ]
from django.shortcuts import render, HttpResponse, redirect, reverse # Create your views here. def index(request): if request.method == 'POST': # 因为是post请求所以需要去把配置文件中中间件的第四行注释掉 i1 = request.POST.get('i1') i2 = request.POST.get('i2') # 注意现在i1和i2是字符串类型 需要先做类型转换 i3 = int(i1) + int(i2) return HttpResponse(i3) return render(request, 'index.html')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <input type="text" id="i1"> + <input type="text" id="i2"> = <input type="text" id="i3"> <button id="d1">按钮</button> {#{* 给按钮添加绑定事件 *}#} <script> $('#d1').click(function () { //获取两个框的内容 朝后端提交异步请求 // ajax基本语法 $.ajax({ //1、到底朝那个后端提交数据 url:'', //控制数据的提交路径 也有三种写法跟form表单的action属性一致 //2、指定当前的请求方式 type:'post', //3、指定提交的数据 data:{'i1':$('#i1').val(), 'i2':$('#i2').val()}, //4、ajax是异步提交 所以需要给一个回调函数来处理返回的结果 success:function (data) {//固定接收一个形参data data就是异步提交的返回结果 {#alert(data)#} //将异步回调的结果通过DOM操作渲染到第三个input框中 $('#i3').val(data) } }) }) </script> </body> </html>
- 演示图
2、ajax基本语法结构
// ajax基本语法
$.ajax({
//1、到底朝那个后端提交数据
url:'', //控制数据的提交路径 也有三种写法跟form表单的action属性一致
//2、指定当前的请求方式
type:'post',
//3、指定提交的数据
data:{'i1':$('#i1').val(), 'i2':$('#i2').val()},
//4、ajax是异步提交 所以需要给一个回调函数来处理返回的结果
success:function (data) {//固定接收一个形参data data就是异步提交的返回结果
{#alert(data)#}
//将异步回调的结果通过DOM操作渲染到第三个input框中
$('#i3').val(data)
}
})
四、content-Type前后端传输数据的编码格式
1、提交数据的三种编码 格式
urlencoded
formdata
application/json
2、演示form与ajax的content-Type参数
a、form 表单
1、默认是以urlencoded编码格式传输数据
2、urlencoded的数据格式
xxx=xxx&yyy=yyy
django 后端针对该格式的数据,会自动解析帮你打包到request.POST中
3、formdata数据格式
django后端针对符合urlencoded编码格式(普通键值对)的数据还是统一解析到request.POST中
而针对formdata文件数据就会自动解析放到request.FILES中
b、ajax提交
1、ajax默认的也是urlencoded编码格式
2、ajax也可以发送json(这个form表单发送不了)
注意:前后端数据交互 编码格式与数据格式一定要一致 ,不可以骗人家!!!明明你要发送的是json格式的数据,你跟我吼一声说你要发送列表了。
application/json
django后端针对json格式的数据,并不会做任何的处理,而是直接放到request.body中, 我们也需要掌握ajax发json格式的基本结构代码
$.ajax({
url:'',
type:'post',
//修改content-Type参数
contentType:'application/json',
data:JSON.stringify({'username':'yafeng', 'password': 123}),//将数据序列化成json格式数据
success:function (data) {
alert(data)
}
})
3、ajax发送文件
借助于内置对象FormData
ajax发送文件的基本语法结构
既发普通键值对也发文件
// ajax发送文件数据 需要借助于内置对象
$('#d3').click(function () {
// 1 需要先生成一个内置对象
var myFormData = new FormData();
// 2 传普通键值对 当普通键值对较多的时候 我们可以利用for循环来添加
myFormData.append('username','jason');
myFormData.append('password',123);
// 3 传文件
myFormData.append('myfile',$('#i1')[0].files[0]); // 获取input框内部用户上传的文件对象
// 发送ajax请求
$.ajax({
url:'',
type:'post',
data:myFormData,
// 发送formdata对象需要指定两个关键性的参数
processData:false, // 让浏览器不要对你的数据进行任何的操作
contentType:false, // 不要使用任何编码格式 对象formdata自带编码格式并且django能够识别该对象
success:function (data) {
alert(data)
}
})
})
- 代码演示
"""day55 URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^ab_ct/', views.ab_ct), ]
from django.shortcuts import render, HttpResponse, redirect, reverse # Create your views here. def index(request): if request.method == 'POST': # 因为是post请求所以需要去把配置文件中中间件的第四行注释掉 i1 = request.POST.get('i1') i2 = request.POST.get('i2') # 注意现在i1和i2是字符串类型 需要先做类型转换 i3 = int(i1) + int(i2) return HttpResponse(i3) return render(request, 'index.html') import json def ab_ct(request): if request.method == 'POST': # print(request.POST) # print(request.FILES) # print(request.body) # 自己处理json格式的数据 # json_bytes = request.body # 先把二进制数据解码 # json_str = json_bytes.decode('utf-8') # 再反序列化 # json_dict = json.loads(json_str) # 注:json.loads可以自动帮你解码,所以上面一步可以简写 # json_dict = json.loads(json_bytes) # 注:json.loads可以自动帮你解码,所以上面一步可以简写 # print(json_dict, type(json_dict)) print(request.POST) print(request.FILES) return render(request, 'ab_ct.html')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> {#<form action="" method="post" enctype="multipart/form-data">#} {# <input type="text" name="username">#} {# <input type="text" name="password">#} {# <input type="file" name="myfile">#} {# <input type="submit">#} {#</form>#} <button id="d1">点击发送ajax请求</button> <button id="d2">ajax发送json数据格式</button> <input type="file" name="myfile" id="i1"> <button id="d3">ajax发送文件数据</button> <script> /*ajax 默认格式 $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username': 'yafeng', 'password': 123}, success:function (data) { alert(data) } }) }) */ //发json格式数据 /* $('#d2').on('click', function () { $.ajax({ url:'', type:'post', //修改content-Type参数 contentType:'application/json', data:JSON.stringify({'username':'yafeng', 'password': 123}),//将数据序列化成json格式数据 success:function (data) { alert(data) } }) }) */ // ajax发送文件数据需要借助于 内置对象FormData $('#d3').click(function () { //1、需要先生成一个内置对象 var myFormDate = new FormData(); //2、传普通键值对 myFormDate.append('username','yafeng'); myFormDate.append('password', 123); //3、传文件 myFormDate.append('myfile', $('#i1')[0].files[0]) //先变成原生json对象 再获取input框内部用户上传的文件对象 //4、发送ajax请求 $.ajax({ url:'', type:'post', data:myFormDate, //发送formdata对象需要指定两个关键字的参数 processData:false,// 让浏览器不要对你的数据进行任何的操作 contentType:false,// 不要使用任何编码格式 对象formdata自带编码格式并且django能够识别该对象 success:function (data) { alert(data) } }) }) </script> </body> </html>
五、Django内置的序列化功能
Django内置的序列功能借助 serializers 模块,可以序列化对象
以前我们将后端将数据传递给前端通过json序列化
from app01 import models import json def xlh(request): book_query = models.Book.objects.all() book_list = [] for book_obj in book_query: book_list.append({ 'name': book_obj.name, 'price': book_obj.price }) res = json.dumps(book_list) return render(request, 'xlh.html', locals())
我们可以使用Django内置的序列化功能模块 serializers ,直接序列化,展示的结果更加多将类名,主键都能拿到
from django.core import serializers def xlh(request): book_query = models.Book.objects.all() res = serializers.serialize('json', book_query) return HttpResponse(res) # [{"model": "app01.book", "pk": 1, "fields": {"name": "jason", "price": "123"}}]
六、批量插入数据
我们之前学过的批量插入数据方式通过for循环再利用create方法,但是每次都要走数据库,效率很低
def insert_data(request): for i in range(1,501): models.Book.objects.create(name='第%s本书'%i) book_query = models.Book.objects.all() return render(request, 'insert.html', locals()) # 每次循环一次都需要去数据库插入一条,500条大约需要一分钟
我们可以通过先将数据添加到一个列表中再使用 bulk_create 方法插入数据,这样就只会走一次数据库,效率很高
def insert_data(request): book_list = [] for i in range(1,9999): book_list.append(models.Book(name='新添加的第%s书'%i)) models.Book.objects.bulk_create(book_list) # 批量插入数据 book_query = models.Book.objects.all() return render(request, 'insert.html', locals()) # 效率很高,大约1秒插入9999条数据
七、自定义分页器及使用
1、先引用分页器组件,封装好的类,存入本地文件夹中
后端代码:
# 获取当前页码,默认为1 current_page = request.GET.get('page', 1) # 获取总页数 all_count = book_queryset.count() # 生成一个自定义分页器类对象 page_obj=Pagination(current_page=current_page,all_count=all_count,pager_count=9) # 针对真实的queryset数据进行切片操作 page_queryset = book_queryset[page_obj.start:page_obj.end] return render(request,'ab_bc.html',locals())
前端代码:
{{ page_obj.page_html|safe }}
八、Ajax结合sweetalert实现删除二次确认
1、首先需要从sweetalert 连接找到Bootstrap-sweetalert包,将包下载到本地
我们只需要使用dist文件中的 js 和 css 文件
2、从sweetalert forBootstrap中找到需要使用的样式,引用其代码
我们以删除二次确认为例:
后端代码:
from django.http import JsonResponse import time def show_user(request): """ 前后端如果是通过ajax进行交互 那么交互的媒介一般情况下都是一个字典 """ if request.method == 'POST': # 模仿删除等待效果 time.sleep(3) # 定义一个字典,通过Ajax传给后端的data back_dic = {"code":1000,'msg':''} # 拿到需要删除的数据id delete_id = request.POST.get('delete_id') # 将数据删除 models.Userinfo.objects.filter(pk=delete_id).delete() back_dic['msg'] = '删除成功,准备跑路!!!' # 以json数据格式将字典返回给前端Ajax中的data return JsonResponse(back_dic) user_queryset = models.Userinfo.objects.all() return render(request,'show_user.html',locals())
前端代码:
首先需要引入sweetalert文件中的css和js文件
{% load static %} <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}"> <script src="{% static 'dist/sweetalert.min.js' %}"></script> {# 删除中文文字宽度不够,找到文字标签修改大小#} <style> div.sweet-alert h2 { padding-top: 10px; } </style>
调用sweetalert中样式代码,加以修改
<script> // 给删除按钮绑定点击事件 $('.cancel').click(function () { // 定义变量指代某一条数据 var $aEle = $(this); // 引用sweetalert中的删除样式代码 swal({ title: "你确定要删吗?", text: "你如果删了,你可要准备跑路啊!", type: "warning", showCancelButton: true, confirmButtonClass: "btn-danger", confirmButtonText: "是的,老子就要删!", cancelButtonText: "惹不起惹不起!", closeOnConfirm: false, closeOnCancel: false, showLoaderOnConfirm: true }, function (isConfirm) { if (isConfirm) { // 发送ajax请求 $.ajax({ url:'', type:'post', data:{'delete_id':$aEle.attr("data_id")}, success:function (data) { // 回调函数会自动将二进制的json格式数据 解码并反序列成js中的数据类型 if (data.code == 1000){ swal("删了!", "你准备跑路吧!", "success"); // 方式1 删除后自动刷新网页 // window.location.reload() // 方式2 DOM操作动态修改,将删除的那条数据移除 $aEle.parent().parent().remove() // 将标签直接移除 }else{ swal('发生了未知的错误', "error"); } } }); } else { swal("怂笔", "你成功的刷新我对你的认知", "error"); } }); }) </script>