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组件
简介
Django
Form
组件有两大功能,用于对页面进行初始化,生成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.默认情况下类中定义好的字段都是必填的 |