django框架(部分讲解)
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)
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
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.默认情况下类中定义好的字段都是必填的