内容回顾
-
前后端数据交互编码方式(contentType)
"""
前后端传输数据编码格式你只需要在知道下面三种即可
urlencoded
formdata
application/json
"""
# get请求无需研究
# 针对form表单
默认是urlencoded
数据格式:username=jason&password=123
django后端针对符合urlencoded编码格式的数据,会自动帮你解析并封装到request.POST中
form表单可以通过enctype来修改编码方式>>>:formdata
form表单即可以发送普通键值也可以发送文件
django后端针对formdata编码格式的数据,会自动将普通键值对还是解析到request.POST,将文件自动解析到request.FILES里面
form表单是无法发送json格式的数据的
# ajax
默认是urlencoded
数据格式:username=jason&password=123
django后端针对符合urlencoded编码格式的数据,会自动帮你解析并封装到request.POST中
-
ajax发送json格式数据
"""
不同程序/应用之间进行数据交互的时候 一定要确保数据格式和编码一致
不要欺骗别人!!!
"""
$.ajax({
url:'',
type:'',
data:JSON.stringify({'username':'jason','password':123}),
dataType:'JSON',
contentType:'application/json',
success:function(args){}
})
# 注意:django后端针对json格式的数据不会做任何的处理,你需要自己去request.body中获取原生的二进制数据自己处理
"""
1.解码
2.反序列化
(json.loads能够自动先解码再反序列化)
"""
-
ajax发送文件
"""
ajax发送文件需要借助于内置对象FormData 会非常的方便
"""
let formDataObj = new FormData(); # 既可以加普通键值也可以加文件
formDataObj.append('username','jason')
formDataObj.append('myfile',$('#i1')[0].files[0])
$.ajax({
url:'',
type:'',
data:formDataObj,
dataType:'JSON',
# 两个关键性的参数
contentType:false,
processData:false,
success:function(args){}
})
# 注意:django后端能够自动识别formdata对象 并且也能够将对象里面包含的普通键值对自己解析并封装到request.POST里面 将文件自动解析封装到request.FILES
"""
request对象方法补充
request.is_ajax()
"""
-
django内置的序列化组件
"""
到了工作中之后 大部分的项目都是前后端分离的
也就意味着我们可能无法直接使用django提供的模版语法来实现前后端数据交互
所以这个时候我们需要将数据处理成大家公共的都能处理的格式(json格式)
一般情况下都是处理成列表套字典的形式[{},{}]
由于针对数据的封装有时候会非常的繁琐 所以有现成的模块可以直接完成
1.django内置的模块
2.第三方模块:django restframework
"""
from django.core import serializers
user_queryset = models.User.objects.all()
res = serializers.serialize('json',user_queryset) # json格式字符串
return HttpResponse(res)
"""
[
{
"models":"app01.user",
"pk":1,
"fields":{
"username":"jason",
...
}
},
{},
{},
]
"""
-
ajax结合sweetalert实现二次确认
"""
我们在工作中写项目的时候其实也是一样的
就是先找现成的模块 代码拷贝过来然后二次修改
当你在一家公司呆了很久,那么这家的业务逻辑基本你都掌握了
"""
# sweetalert针对中文可能会出现展示补全的现象
自己找到对应的标签书写css修改样式即可
# 你也可以不结合sweetalert就用ajax和BOM操作也能完成二次确认
res = confirm()
if(res){
$.ajax({})
}else{
不发
}
-
批量插入
"""
当频繁的走数据库操作的时候 效率会呈现指数型下降
"""
user_list = []
for i in range(100000):
user_list.append(models.User(title='%s'%i))
models.User.objects.bulk_create(user_list)
-
自定义分页器
"""
django也有内置的分页器模块 但是功能较少代码繁琐不便于使用
所以我们自己自定义我们自己的分页器
"""
1.queryset对象是直接切片操作的
2.用户到底要访问哪一页 如何确定? url?page=1
current_page = request.GET.get('page',1)
# 获取到的数据都是字符串类型 你需要注意类型转换
3.自己规定每页展示多少条数据
per_page_num = 10
4.切片的起始位置和终止位置
start_page = (current_page - 1)* per_page_num
end_page = current_page * per_page_num
# 利用简单找规律 找出上述四个参数的规律
5.当前数据的总条数
book_queryset.count()
6.如何确定总共需要多少页才能展示完所有的数据
# 利用python内置函数divmod()
page_count, more = divmod(all_count,per_page_num)
if more:
page_count += 1
7.前端模版语法是没有range功能的
# 前端代码不一定非要在前端书写 也可以在后端生成传递给页面
8.针对需要展示的页码需要你自己规划好到底展示多少个页码
# 一般情况下页码的个数设计都是奇数(符合审美标准) 11个页码
当前页减5
当前页加6
你可以给标签价样式从而让选中的页码高亮显示
9.针对页码小于6的情况 你需要做处理 不能再减
自定义分页器推导到第九部就可以 无需你继续推到了 代码也无需掌握
内容概要
-
自定义分页器的拷贝及使用
-
校验性组件:form组件
组件有很多很多,就类似于功能模块
自定义分页器的拷贝及使用
"""
当我们需要使用到非django内置的第三方功能或者组件代码的时候
我们一般情况下会创建一个名为utils文件夹 在该文件夹内对模块进行功能性划分
utils可以在每个应用下创建 具体结合实际情况
我们到了后期封装代码的时候 不再局限于函数
还是尽量朝面向对象去封装
我们自定义的分页器是基于bootstrap样式来的 所以你需要提前导入bootstrap
bootstrap 版本 v3
jQuery 版本 v3
"""
# 后端
book_queryset = models.Book.objects.all()
current_page = request.GET.get('page',1)
all_count = book_queryset.count()
# 1 传值生成对象
page_obj = Pagination(current_page=current_page,all_count=all_count)
# 2 直接对总数据进行切片操作
page_queryset = book_queryset[page_obj.start:page_obj.end]
# 3 将page_queryset传递到页面 替换之前的book_queryset
# 前端
{% for book_obj in page_queryset %}
<p>{{ book_obj.title }}</p>
<nav aria-label="Page navigation">
</nav>
{% endfor %}
{#利用自定义分页器直接显示分页器样式#}
{{ page_obj.page_html|safe }}
"""
你们只需要掌握如何拷贝使用 以及大致的推导思路即可
"""
前戏
"""
写一个注册功能
获取用户名和密码 利用form表单提交数据
在后端判断用户名和密码是否符合一定的条件
用户名中不能含有xxx
密码不能少于三位
如何符合条件需要你将提示信息展示到前端页面
"""
def ab_form(request):
back_dic = {'username':'','password':''}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if 'xxx' in username:
back_dic['username'] = '不符合社会主义核心价值观'
if len(password) < 3:
back_dic['password'] = '不能太短 不好!'
"""
无论是post请求还是get请求
页面都能够获取到字典 只不过get请求来的时候 字典值都是空的
而post请求来之后 字典可能有值
"""
return render(request,'ab_form.html',locals())
<form action="" method="post">
<p>username:
<input type="text" name="username">
<span style="color: red">{{ back_dic.username }}</span>
</p>
<p>password:
<input type="text" name="password">
<span style="color: red">{{ back_dic.password }}</span>
</p>
<input type="submit" class="btn btn-info">
</form>
"""
1.手动书写前端获取用户数据的html代码 渲染html代码
2.后端对用户数据进行校验 校验数据
3.对不符合要求的数据进行前端提示 展示提示信息
forms组件
能够完成的事情
1.渲染html代码
2.校验数据
3.展示提示信息
为什么数据校验非要去后端 不能在前端利用js直接完成呢?
数据校验前端可有可无
但是后端必须要有!!!
因为前端的校验是弱不禁风的 你可以直接修改
或者利用爬虫程序绕过前端页面直接朝后端提交数据
购物网站
选取了货物之后 会计算一个价格发送给后端 如果后端不做价格的校验
实际是获取到用户选择的所有商品的主键值
然后在后端查询出所有商品的价格 再次计算一遍
如果跟前端一致 那么完成支付如果不一致直接拒绝
"""
基本使用
from django import forms
class MyForm(forms.Form):
# username字符串类型最小3位最大8位
username = forms.CharField(min_length=3,max_length=8)
# password字符串类型最小3位最大8位
password = forms.CharField(min_length=3,max_length=8)
# email字段必须符合邮箱格式 xxx@xx.com
email = forms.EmailField()
校验数据
"""
1.测试环境的准备 可以自己拷贝代码准备
2.其实在pycharm里面已经帮你准备一个测试环境
python console
"""
from app01 import views
# 1 将带校验的数据组织成字典的形式传入即可
form_obj = views.MyForm({'username':'jason','password':'123','email':'123'})
# 2 判断数据是否合法 注意该方法只有在所有的数据全部合法的情况下才会返回True
form_obj.is_valid()
False
# 3 查看所有校验通过的数据
form_obj.cleaned_data
{'username': 'jason', 'password': '123'}
# 4 查看所有不符合校验规则以及不符合的原因
form_obj.errors
{
'email': ['Enter a valid email address.']
}
# 5 校验数据只校验类中出现的字段 多传不影响 多传的字段直接忽略
form_obj = views.MyForm({'username':'jason','password':'123','email':'123@qq.com','hobby':'study'})
form_obj.is_valid()
True
# 6 校验数据 默认情况下 类里面所有的字段都必须传值
form_obj = views.MyForm({'username':'jason','password':'123'})
form_obj.is_valid()
False
"""
也就意味着校验数据的时候 默认情况下数据可以多传但是绝不可能少传
"""
渲染标签
"""
forms组件只会自动帮你渲染获取用户输入的标签(input select radio checkbox)
不能帮你渲染提交按钮
"""
def index(request):
# 1 先产生一个空对象
form_obj = MyForm()
# 2 直接将该空对象传递给html页面
return render(request,'index.html',locals())
# 前端利用空对象做操作
<p>第一种渲染方式:代码书写极少,封装程度太高 不便于后续的扩展 一般情况下只在本地测试使用</p>
{{ form_obj.as_p }}
{{ form_obj.as_ul }}
{{ form_obj.as_table }}
<p>第二种渲染方式:可扩展性很强 但是需要书写的代码太多 一般情况下不用</p>
<p>{{ form_obj.username.label }}:{{ form_obj.username }}</p>
<p>{{ form_obj.password.label }}:{{ form_obj.password }}</p>
<p>{{ form_obj.email.label }}:{{ form_obj.email }}</p>
<p>第三种渲染方式(推荐使用):代码书写简单 并且扩展性也高</p>
{% for form in form_obj %}
<p>{{ form.label }}:{{ form }}</p>
{% endfor %}
"""
label属性默认展示的是类中定义的字段首字母大写的形式
也可以自己修改 直接给字段对象加label属性即可
username = forms.CharField(min_length=3,max_length=8,label='用户名')
"""
展示提示信息
"""
浏览器会自动帮你校验数据 但是前端的校验弱不禁风
如何让浏览器不做校验
<form action="" method="post" novalidate>
"""
def index(request):
# 1 先产生一个空对象
form_obj = MyForm()
if request.method == 'POST':
# 获取用户数据并且校验
"""
1.数据获取繁琐
2.校验数据需要构造成字典的格式传入才行
ps:但是request.POST可以看成就是一个字典
"""
# 3.校验数据
form_obj = MyForm(request.POST)
# 4.判断数据是否合法
if form_obj.is_valid():
# 5.如果合法 操作数据库存储数据
return HttpResponse('OK')
# 5.不合法 有错误
# 2 直接将该空对象传递给html页面
return render(request,'index.html',locals())
{% for form in form_obj %}
<p>
{{ form.label }}:{{ form }}
<span style="color: red">{{ form.errors.0 }}</span>
</p>
{% endfor %}
"""
1.必备的条件 get请求和post传给html页面对象变量名必须一样
2.forms组件当你的数据不合法的情况下 会保存你上次的数据 让你基于之前的结果进行修改
更加的人性化
"""
# 针对错误的提示信息还可以自己自定制
class MyForm(forms.Form):
# username字符串类型最小3位最大8位
username = forms.CharField(min_length=3,max_length=8,label='用户名',
error_messages={
'min_length':'用户名最少3位',
'max_length':'用户名最大8位',
'required':"用户名不能为空"
}
)
# password字符串类型最小3位最大8位
password = forms.CharField(min_length=3,max_length=8,label='密码',
error_messages={
'min_length': '密码最少3位',
'max_length': '密码最大8位',
'required': "密码不能为空"
}
)
# email字段必须符合邮箱格式 xxx@xx.com
email = forms.EmailField(label='邮箱',
error_messages={
'invalid':'邮箱格式不正确',
'required': "邮箱不能为空"
}
)
钩子函数(HOOK)
"""
在特定的节点自动触发完成响应操作
钩子函数在forms组件中就类似于第二道关卡,能够让我们自定义校验规则
在forms组件中有两类钩子
1.局部钩子
当你需要给单个字段增加校验规则的时候可以使用
2.全局钩子
当你需要给多个字段增加校验规则的时候可以使用
"""
# 实际案例
# 1.校验用户名中不能含有666 只是校验username字段 局部钩子
# 2.校验密码和确认密码是否一致 password confirm两个字段 全局钩子
# 钩子函数 在类里面书写方法即可
# 局部钩子
def clean_username(self):
# 获取到用户名
username = self.cleaned_data.get('username')
if '666' in username:
# 提示前端展示错误信息
self.add_error('username','光喊666是不行滴~')
# 将钩子函数钩去出来数据再放回去
return username
# 全局钩子
def clean(self):
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if not confirm_password == password:
self.add_error('confirm_password','两次密码不一致')
# 将钩子函数钩出来数据再放回去
return self.cleaned_data
label 字段名
error_messages 自定义报错信息
initial 默认值
required 控制字段是否必填
"""
1.字段没有样式
2.针对不同类型的input如何修改
text
password
date
radio
checkbox
...
"""
widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'})
# 多个属性值的话 直接空格隔开即可
# 第一道关卡里面还支持正则校验
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
]
其他类型渲染
# radio
gender = forms.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
# select
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
# 多选
hobby1 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
# 单选checkbox
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
# 多选checkbox
hobby2 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)