CSIC_716_2020110【Django入门---多对多表关系的三种创建方式、Ajax、自定义分页器、删除的二次弹窗确认】
多对多表关系的建立方式(以图书表和作者表为例)
多对多表关系的创建有三种方式:自动创建、手动创建、半自动创建
前文中已经介绍了多对多的全自动创建方式,但是创建的第三张表中只有两个外键字段,因为是该表是django自动生成的,故没办法在第三张表中增加其余字段
本文介绍剩余的两种:
纯手动创建以及半自动创建
纯手动创建
特点:多对多的第三张关系表是自己手动创建的,相较于全自动创建而言,在关系表中除外键外还可以添加任意字段。
不足:在跨表查询的时候比较麻烦,不可以使用django orm提供的便利方法
class Book(models.Model): title = models.CharField(max_length= 32 ) price = models.DecimalField(max_digits=8, max_places=2) class Author(models.Model): name = models.CharField(max_length = 32) age = models.IntegerField() class Book2Author(models.Model): book = models.ForeignKey(to = 'Book') author = models.ForeignKey(to = 'Author') remark = models.CharField(max_length = 32)
半自动创建
外键建在book表中 注意外键括号内参数的区别
class Book(models.Model): title = models.CharField(max_length= 32 ) price = models.DecimalField(max_digits=8, max_places=2) authors = models.ManyToMany(to='Author', through=‘Book2Author’,through_fields=('book','author')) class Author(models.Model): name = models.CharField(max_length = 32) age = models.IntegerField() class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to ='Author') remark = models.CharField(max_length = 32)
外键建在author中
class Book(models.Model): title = models.CharField(max_length= 32 ) price = models.DecimalField(max_digits=8, max_places=2) class Author(models.Model): name = models.CharField(max_length = 32) age = models.IntegerField() books= models.ManyToMany(to='Book', through=‘Book2Author’,through_fields=('author','book')) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to ='Author') remark = models.CharField(max_length = 32)
优点:扩展性强,可以在关系表总增加任意字段,并且还可以使用orm提供的跨表查询的方法,推荐
缺点: orm提供的多对多外键的增删改查没办法使用了即无法使用 add、set、remove、clear四个方法无法使用。
Ajax
ajax的特点:异步提交,局部刷新。Ajax是通过js语言写的一个功能模块,本文学习的是通过jQuery封装好的ajax模块
目前本人已知的与后端交互的方式有如下四种:
1、浏览器窗口中直接写网址:请求方式GET;
2、<a>标签中的 href后面填写url :请求方式GET;
3、form表单 : GET和POST请求都可以;
4、Ajax: GET和POST请求都可以;
Ajax的基本语法结构:
四个步骤:向哪个地址提交、以什么请求方式、提交什么内容、返回的结果如何处理
$.ajax({ urls:' ', #可以填具体的url,也可以填地址的后缀,也可以不填默认提交到当前 type:' ', # type的类型有get和post data:{ }, # 必须要保证是个字典的样式传 success:function(data):{ #data为后端处理后传到前端的值 代码块 代码块 } })
form表单默认的是以urlencoded的编码格式传输数据,后端接收到数据后会先存入request.body,然后再将键值对数据自动存到request.POST中;当表单中有普通键值对和文件时,就需要修改enctype属性,以formdata编码格式传输数据,后端接收到数据后会先存入request.body,然后再将普通键值对存到request.POST中,将文件数据存入request.FILES中。
Ajax也是默认以urlencoded方式传输数据,Ajax中有一个contentType属性可以指定编码方式,除了urlencoded外还支持application/json,但是如果明确了contentType是application/json时,data后面的数据就一定要是json类型的数据,可以借助Javascript中自带的序列化方法JSON.stringify,后端接收到的数据以二进制形式存放在request.body中,后端可以通过json.loads(request.body)取值,loads可以直接将符合json格式的二进制数据反序列化,举例如下:
$.ajax({ urls:' ' , type:' ' , contentType:'application/json' , data:JSON.stringify({ }) , success:function(data):{ 代码块 } })
需要注意的是,如果前端以json形式发到后端,后端收到值后,会存放到request.body中,取j值直接从request.body中取,取出来的是一组二进制的json数据。
这时候按照套路应该先解码再反序列化。但是在后端,json.loads(二进制json)会直接先解码再反序列化。
)
Ajax发送文件数据
这里需要使用JavaScript的内置对象FormData,它既可以存键值对也能存文件。
$(#d1).on('click', function(){ //给id为d1的按钮绑定click方法 var myFormData = new FormData(); myFormData.append( 'key', 'value' ) ; //普通键值写入的方法 myFormData.append( 'key', '$(#d1)[0]files[0]'); //数据组织完了,下面开始Ajax操作 $.ajax({ url:' ', type:'post' , data:myFormData, //发送FormData对象需要指定下面两个关键的参数 processData:false, //让浏览器不要动数据 contentType: false, //不使用编码格式,因为FormData自带django能识别的编码格式 success:function(data){ 代码块 } }) })
django内置的序列化功能 后期会介绍drf
在线JSON校验格式化工具 BeJSON
序列化模块 serializers
from django.core import serializers
res = serializers.serialize('json' , queryset) // 第一个参数是明确序列化成json格式,第二个参数是提供一个queryset对象
批量向数据库中插入数据的方式
使用 models.Book.objects.bulk_create(对象列表)
def bulk_create(request): list_obj = [] #通过for循环生成对象,再将对象append进list_obj中 for i in range(1000): list_obj.append(models.Book(title = '第%s本书'% i)) models.Book.objects.bulk_create(list_obj) book_queryset = models.Book.objects.all() return ....
自定义分页器
略
1 class Pagination(object): 2 def __init__(self, current_page, all_count, per_page_num=10, pager_count=11): 3 """ 4 封装分页相关数据 5 :param current_page: 当前页 6 :param all_count: 数据库中的数据总条数 7 :param per_page_num: 每页显示的数据条数 8 :param pager_count: 最多显示的页码个数 9 10 用法: 11 queryset = model.objects.all() 12 page_obj = Pagination(current_page,all_count) 13 page_data = queryset[page_obj.start:page_obj.end] 14 获取数据用page_data而不再使用原始的queryset 15 获取前端分页样式用page_obj.page_html 16 """ 17 try: 18 current_page = int(current_page) 19 except Exception as e: 20 current_page = 1 21 22 if current_page < 1: 23 current_page = 1 24 25 self.current_page = current_page 26 27 self.all_count = all_count 28 self.per_page_num = per_page_num 29 30 # 总页码 31 all_pager, tmp = divmod(all_count, per_page_num) 32 if tmp: 33 all_pager += 1 34 self.all_pager = all_pager 35 36 self.pager_count = pager_count 37 self.pager_count_half = int((pager_count - 1) / 2) 38 39 @property 40 def start(self): 41 return (self.current_page - 1) * self.per_page_num 42 43 @property 44 def end(self): 45 return self.current_page * self.per_page_num 46 47 def page_html(self): 48 # 如果总页码 < 11个: 49 if self.all_pager <= self.pager_count: 50 pager_start = 1 51 pager_end = self.all_pager + 1 52 # 总页码 > 11 53 else: 54 # 当前页如果<=页面上最多显示11/2个页码 55 if self.current_page <= self.pager_count_half: 56 pager_start = 1 57 pager_end = self.pager_count + 1 58 59 # 当前页大于5 60 else: 61 # 页码翻到最后 62 if (self.current_page + self.pager_count_half) > self.all_pager: 63 pager_end = self.all_pager + 1 64 pager_start = self.all_pager - self.pager_count + 1 65 else: 66 pager_start = self.current_page - self.pager_count_half 67 pager_end = self.current_page + self.pager_count_half + 1 68 69 page_html_list = [] 70 # 添加前面的nav和ul标签 71 page_html_list.append(''' 72 <nav aria-label='Page navigation>' 73 <ul class='pagination'> 74 ''') 75 first_page = '<li><a href="?page=%s">首页</a></li>' % (1) 76 page_html_list.append(first_page) 77 78 if self.current_page <= 1: 79 prev_page = '<li class="disabled"><a href="#">上一页</a></li>' 80 else: 81 prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,) 82 83 page_html_list.append(prev_page) 84 85 for i in range(pager_start, pager_end): 86 if i == self.current_page: 87 temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,) 88 else: 89 temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,) 90 page_html_list.append(temp) 91 92 if self.current_page >= self.all_pager: 93 next_page = '<li class="disabled"><a href="#">下一页</a></li>' 94 else: 95 next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,) 96 page_html_list.append(next_page) 97 98 last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,) 99 page_html_list.append(last_page) 100 # 尾部添加标签 101 page_html_list.append(''' 102 </nav> 103 </ul> 104 ''') 105 return ''.join(page_html_list)
current_page = request.GET.get('page', 1) all_count = book_queryset.count() # 1 现生成一个自定义分页器类对象 page_obj = Pagination(current_page=current_page,all_count=all_count,pager_count=9) # 2 针对真实的queryset数据进行切片操作 page_queryset = book_queryset[page_obj.start:page_obj.end]
{% for book_obj in page_queryset %} <p>{{ book_obj.title }}</p> {% endfor %} {{ page_obj.page_html|safe }}
Ajax结合Sweetalert实现删除二次确认
代码
顶