ajax补充说明
主要是针对回调函数args接收到的响应数据
request.is_ajax()判断是不是ajax请求

- 后端返回的三板斧都会被
args接收不再影响整个浏览器页面


- 选择使用
ajax做前后端交互的时候 后端一般返回的都是字典数据
| user_dict = {'code': 10000, 'username': '小阳人', 'hobby': '哎呦喂~'} |
ajax自动反序列化后端的json格式的bytes类型数据

多对多三种创建方式
方式1:全自动创建
| class Book(models.Model): | |
| title = models.CharField(max_length=32) | |
| authors = models.ManyToManyField(to='Author') | |
| class Author(models.Model): | |
| name = models.CharField(max_length=32) |
优势:自动创建第三张表 并且提供了add、remove、set、clear四种操作
劣势:第三张表无法创建更多的字段 扩展性较差
方式2:纯手动创建(不推荐)
| class Book(models.Model): | |
| title = models.CharField(max_length=32) | |
| class Author(models.Model): | |
| name = models.CharField(max_length=32) | |
| class Book2Author(models.Model): | |
| book = models.ForeignKey(to='Book') | |
| author = models.ForeignKey(to='Author') | |
| others = models.CharField(max_length=32) | |
| join_time = models.DateField(auto_now_add=True) |
优势:第三张表完全由自己创建 扩展性强
劣势:编写繁琐 并且不再支持add、remove、set、clear以及正反向概念
方式3:半自动创建(推荐使用)
| class Book(models.Model): | |
| title = models.CharField(max_length=32) | |
| authors = models.ManyToManyField(to='Author', | |
| through='Book2Author', through_fields=('book','author') | |
| ) | |
| class Author(models.Model): | |
| name = models.CharField(max_length=32) | |
| class Book2Author(models.Model): | |
| book = models.ForeignKey(to='Book', on_delete=models.CASCADE) | |
| author = models.ForeignKey(to='Author', on_delete=models.CASCADE) | |
| others = models.CharField(max_length=32) | |
| join_time = models.DateField(auto_now_add=True) |
优势:第三张表完全由自己创建 扩展性强 正反向概念依然清晰可用
劣势:编写繁琐不再支持add、remove、set、clear
django内置序列化组件(drf前身)
这里的内置序列化组件,其实就是实现将后端数据,存放到字典中或是存放到列表中有序输出
| 这里是我们用JsonResponse模块自己实现的代码 | |
| '''前后端分离的项目 视图函数只需要返回json格式的数据即可''' | |
| from app01 import models | |
| from django.http import JsonResponse | |
| def ab_ser_func(request): | |
| # 1.查询所有的书籍对象 | |
| book_queryset = models.Book.objects.all() # queryset [对象、对象] | |
| # 2.封装成大字典返回 | |
| data_dict = {} | |
| for book_obj in book_queryset: | |
| temp_dict = {} | |
| temp_dict['pk'] = book_obj.pk | |
| temp_dict['title'] = book_obj.title | |
| temp_dict['price'] = book_obj.price | |
| temp_dict['info'] = book_obj.info | |
| data_dict[book_obj.pk] = temp_dict # {1:{},2:{},3:{},4:{}} | |
| return JsonResponse(data_dict) | |
| 序列化组件(django自带的,后续会学更厉害的drf) | |
| # 导入内置序列化模块 | |
| from django.core import serializers | |
| # 调用该模块下的方法,第一个参数是你想以什么样的方式序列化你的数据 | |
| res = serializers.serialize('json', book_queryset) | |
| return HttpResponse(res) |
使用序列化模块,不仅节省代码,同时他封装的更精致

批量操作数据
| 当我们需要大批量创建数据的时候 如果一条一条的去创建或许需要猴年马月 我们可以先试一试for循环试试 | |
| html | |
| <div class="col-md-10 col-md-offset-2"> | |
| {% for book_obj in book_query %} | |
| <p class="text-center">{{ book_obj.name }}</p> | |
| {% endfor %} | |
| </div> | |
| views | |
| def home(request): | |
| for i in range(1000): | |
| models.Books.objects.create(name=f'第{i}本书') | |
| ''' | |
| 浏览器访问一个django路由 立刻创建10万条数据并展示到前端页面 | |
| 涉及到大批量数据的创建 直接使用create可能会造成数据库崩溃 | |
| 所以Django就有一个专门来创建的参数就是 Dulk.create 效率极高 还有 Dulk.update | |
| ''' | |
| book_list = [] | |
| for i in range(10000): | |
| book_obj = models.Books(name=f'第{i}本书') | |
| book_list.append(book_obj) | |
| ''' 上述代码可以简化为一行''' | |
| [models.Books(name=f'第{i}本书') for i in range(10000)] | |
| models.Books.objects.bulk_create(book_list) # 批量创建数据 | |
| models.Books.objects.bulk_update(book_list) # 批量修改数据 | |
| book_query = models.Books.objects.all() | |
| return render(request, 'booklist.html', locals()) | |
| '''这个时候快速的批量创建一下就好了 使用普通的create创建还需要等待''' |

Django批量数据展示
| 到此目前我们的数据都在一页 鼠标滑动都要很久 我们正常应该是给这个数据分页 一个多少条数据 所以我们进行分页 | |
| book_query = models.Books.objects.all()[0:10] | |
| ''' all返回的是QuerySet 可以看成列表套对象 也就是说支持索引取值 现在的问题总不可能让客户用源码修改页数吧?''' | |
| current_page = request.GET.get('page', 1) # 获取用户指定的page 如果没有则默认展示第一页 | |
| try: | |
| current_page = int(current_page) # 因为返回给前端的是字符串要转换成整形 | |
| except ValueError: | |
| current_page = 1 | |
| per_page_num = 10 # 自定义每一页展示的数据条数 | |
| start_num = (current_page - 1) * per_page_num # 定义出切片的起始位置 | |
| end_num = current_page * per_page_num # 定义出切片的终止位置 | |
| book_query = models.Books.objects.all()[start_num:end_num] | |
| # http://127.0.0.1:8000/booklist/?page=4 | |
| '''现在可以通过地址栏输入对应的页码就可以进入对于的页面 但是总不能让用户一直输入页数吧 体验极差 正常网站都有页码点击跳转''' | |
| {% for book_obj in book_query %} | |
| <p class="text-center">{{ book_obj.name }}</p> | |
| {% endfor %} | |
| <nav aria-label="Page navigation" class="text-center"> | |
| <ul class="pagination"> | |
| <li> | |
| <a href="#" aria-label="Previous"> | |
| <span aria-hidden="true">«</span> | |
| </a> | |
| </li> | |
| <li class="active"><a href="?page=1">1</a></li> | |
| <li><a href="?page=2">2</a></li> | |
| <li><a href="?page=3">3</a></li> | |
| <li><a href="?page=4">4</a></li> | |
| <li><a href="?page=5">5</a></li> | |
| <li> | |
| <a href="#" aria-label="Next"> | |
| <span aria-hidden="true">»</span> | |
| </a> | |
| </li> | |
| </ul> | |
| </nav> | |
| '''现在有点像模像样了 但是还是有一个问题就是当前只能获取这五页后面的跳转不过去 我们也不知道总共有多少页''' |

| 所以我们要计算出来总共有多少页 但是也不能把前端页码行数写死了 需要通过循环来实现 | |
| def home(request): | |
| book_data = models.Books.objects.all() # 获取所有的数据 | |
| all_count = book_data.count() # 通过计数获取有多少条数据 | |
| per_page_num = 10 # 自定义每一页展示的数据条数 这个时候就可以动态计算了 | |
| all_page_num, more = divmod(all_count, per_page_num) # divmod计算需要多少页来展示 | |
| if more: | |
| all_page_num += 1 # 这样就获取到了所有页码的数量 | |
| current_page = request.GET.get('page', 1) # 获取用户指定的page 如果没有则默认展示第一页 | |
| try: | |
| current_page = int(current_page) # 因为返回给前端的是字符串要转换成整形 | |
| except ValueError: | |
| current_page = 1 | |
| html_page = '' | |
| xxx = current_page | |
| if current_page < 6: # 一用户访问的页码小于6就等6 | |
| xxx = 6 | |
| for i in range(xxx-5, xxx+6): | |
| if current_page == i: | |
| html_page += '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i) | |
| else: | |
| html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i) | |
| start_num = (current_page - 1) * per_page_num # 定义出切片的起始位置 | |
| end_num = current_page * per_page_num # 定义出切片的终止位置 | |
| book_query = models.Books.objects.all()[start_num:end_num] | |
| ''' all返回的是QuerySet 可以看成列表套对象 也就是说支持索引取值 | |
| 现在的问题总不可能让客户用源码修改页数吧? | |
| ''' | |
| return render(request, 'booklist.html', locals()) | |
| 这个时候我们的分页器就差不多完美了下面是整体思路解析!!! | |
| ''' | |
| 当数据量比较大的时候 页面展示应该考虑分页 | |
| 1.QuerySet切片操作 | |
| 2.分页样式添加 | |
| 3.页码展示 | |
| 如何根据总数据和每页展示的数据得出总页码 | |
| divmod() | |
| 4.如何渲染出所有的页码标签 | |
| 前端模板语法不支持range 但是后端支持 我们可以在后端创建好html标签然后传递给html页面使用 | |
| 5.如何限制住展示的页面标签个数 | |
| 页码推荐使用奇数位(对称美) 利用当前页前后固定位数来限制 | |
| 6.首尾页码展示范围问题 | |
| 上述是分页器组件的推导流程 我们无需真正编写 | |
| django自带一个分页器组件 但是不太好用 我们自己也写了一个 | |
| ''' |

分页器思路
| 分页器主要听处理逻辑 代码最后很简单 | |
| 推导流程 | |
| 1.queryset支持切片操作(正数) | |
| 2.研究各个参数之间的数学关系 | |
| 每页固定展示多少条数据、起始位置、终止位置 | |
| 3.自定义页码参数 | |
| current_page = request.GET.get('page') | |
| 4.前端展示分页器样式 | |
| 5.总页码数问题 | |
| divmod方法 | |
| 6.前端页面页码个数渲染问题 | |
| 后端产生 前端渲染 |
自定义分页器的使用
创建一个独立py文件
| class Pagination(object): | |
| def __init__(self, current_page, all_count, per_page_num=10, 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) | |
| def start(self): | |
| return (self.current_page - 1) * self.per_page_num | |
| 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 class="center"><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) | |
| html | |
| {% for book_obj in page_query %} | |
| <p class="text-center">{{ book_obj.name }}</p> | |
| {% endfor %} | |
| {{ page_obj.page_html|safe }} | |
| views | |
| def home(request): | |
| from app01.plugins import mypage | |
| book_query = models.Books.objects.all() | |
| page_obj = mypage.Pagination(current_page=request.GET.get('page'), | |
| all_count=book_query.count() | |
| ) | |
| page_query = book_query[page_obj.start:page_obj.end] | |
| return render(request, 'booklist.html', locals()) |

form组件
简介
DjangoForm组件有两大功能,用于对页面进行初始化,生成HTML标签,此外还可以对用户提交对数据进行校验(显示错误信息)
| 小需求:获取用户数据并发送给后端校验 后端返回不符合校验规则的提示信息 | |
| form组件 | |
| 1.自动校验数据 | |
| 2.自动生成标签 | |
| 3.自动展示信息 | |
| from django import forms | |
| class MyForm(forms.Form): | |
| username = forms.CharField(min_length=3, max_length=8) # username字段最少三个字符最大八个字符 | |
| age = forms.IntegerField(min_value=0, max_value=200) # 年龄最小0 最大200 | |
| email = forms.EmailField() # 必须符合邮箱格式 | |
| 校验数据的功能(初识) | |
| form_obj = views.MyForm({'username':'jason','age':18,'email':'123'}) | |
| form_obj.is_valid() # 1.判断数据是否全部符合要求 | |
| False # 只要有一个不符合结果都是False | |
| form_obj.cleaned_data # 2.获取符合校验条件的数据 | |
| {'username': 'jason', 'age': 18} | |
| form_obj.errors # 3.获取不符合校验规则的数据及原因 | |
| {'email': ['Enter a valid email address.']} | |
| 1.只校验类中定义好的字段对应的数据 多传的根本不做任何操作 | |
| 2.默认情况下类中定义好的字段都是必填的 |

