28 Jun 18
一、内容回顾
1. AJAX(前端向后端发送请求的方式之一)
前端向后端发送请求的四种方式:
a. 直接在浏览器地址栏输入URL访问--> GET
b. 点击a标签跳转到指定页面 --> GET
c. form表单 --> GET/POST
d. AJAX --> GET/POST
注意:
a. 单独的input没有办法向后端发送请求
b. 以上四种方式相对独立;FORM按照FORM的方式,AJAX按照AJAX的方式(对于AJAX,后端只能取到data中的数据)
2. jQuery版AJAX
a. 一般请求
$.ajax({
url: "/index/", // 往哪里发送请求
type: "POST", // 请求的方法
data: {name:"张曌",age:16}, // 请求的数据
success:function(data){
// 请求被正常响应时自动执行的回调函数
console.log(data)
}
})
b. 提交携带文件类型的数据(BBS中用户上传头像时会用到)
var formData = new FormData(); // 生成一个FormData对象
var fileObj = $("#f1")[0].files[0] // 得到用户选中的文件对象
formData.append("f1", fileObj) // 向formData对象中添加键值对数据
$.ajax({
url: "/index/", // 往哪里发送请求
type: "POST", // 请求的方法
processData: false, // 不让jQuery处理我的数据
contentType: false, // 不让jQuery设置请求头
data: formData,
success:function(data){
// 请求被正常响应时自动执行的回调函数
console.log(data)
}
})
c. AJAX处理CSRF_TOKEN的三种方式
1)手动把csrf_token的值取到,塞进请求的data中
2)从cookie中取csrf_token,塞进请求头中
3)在一个单独的JS文件中,给$.ajax统一设置一下请求头
d. 注意: 如果发送的data中,数据不是简单的字符串或数字,需要用JSON.stringify()转换成JSON字符串
3. 序列化(serializer):快速把ORM对象转换成JSON格式的数据
4. 作业讲解(注册时的用户名查重,登陆成功后的跳转)
# views.py
from django.shortcuts import render, redirect
from app01 import models
from django.http import JsonResponse
def register(request):
return render(request, "register.html")
def check_name(request):
if request.method == "POST":
ret = {"code": 0}
username = request.POST.get("name")
is_exist = models.User.objects.filter(name=username)
if is_exist:
ret = {"code": 1, "errMsg": "用户名已存在!"}
return JsonResponse(ret)
def login(request):
if request.method == "POST":
ret = {"code": 0}
name = request.POST.get("name")
pwd = request.POST.get("pwd")
ok = models.User.objects.filter(name=name, pwd=pwd)
if not ok:
ret["code"] = 1
ret["data"] = "用户名或密码错误"
else:
ret["data"] = "http://www.luffycity.com"
return JsonResponse(ret)
# 注意不能用return redirect("http://www.luffycity.com");redirect()这个响应浏览器可以解析,但AJAX不能解析
# AJAX 只可以处理HttpResponse()或JsonResponse()这系列响应
return render(request, "login.html")
# register.html
<body>
<p>
用户名:<input type="text" id="i1">
<span class="error" id="s1"></span>
</p>
<p>
密码:<input type="password" id="i2">
</p>
<p>
<button id="b1">注册</button>
</p>
<script src="/static/jquery-3.3.1.min.js"></script>
<script src="/static/setupAjax.js"></script>
<script>
{# input 只能用.on("input", function () {}这种形式#}
{# 如果想失去光标时触发,用blur,可以直接.blur, 也可以.on("blur", function () {} #}
$("#i1").on("input", function () {
$("#s1").text("");
{#每次触发input时,先清空error span中的数据,这样如果数据库中有用户名'zhangzhao','zhangzhao1'依旧可以作为新用户名被使用#}
var value = $(this).val();
$.ajax({
url: "/check_name/",
type: "POST",
{#这里如果是POST,后端必须用POST方法取数据#}
data: {name: value},
success:function (data) {
console.log(data);
if (data.code){
$("#s1").text(data.errMsg);
}
}
})
})
</script>
</body>
# login.html
<body>
<p>
用户名:<input type="text" id="i1">
<span class="error" id="s1"></span>
</p>
<p>
密码:<input type="password" id="i2">
</p>
<p>
<button id="b1">登录</button>
</p>
<script src="/static/jquery-3.3.1.min.js"></script>
<script src="/static/setupAjax.js"></script>
<script>
$("#b1").click(function () {
var name = $("#i1").val();
var pwd = $("#i2").val();
$.ajax({
url: "/login/",
type: "POST",
data: {name: name, pwd: pwd},
success:function (data) {
if (!data.code){
location.href = data.data;
{# location.href 等同于windows.location.href,是用js代码实现跳转的方式#}
}
}
})
})
</script>
</body>
二、今日内容(form表单)
http://www.cnblogs.com/liwenzhou/p/8747872.html
1. form组件: 类似orm,一个form类对应一个form表单;可以帮助处理以下三方面需求
a. HTML文件由form类的对象自己生成 (只能生成获取用户信息的那些input标签(不包含form,csrt_token, submit等))
b. 对提交过来的数据做校验,返回错误提示信息
c. 在页面中保留用户原来填写的信息 (自己手写form表达,用户之前的填写若想保留处理起来比较麻烦)
2. form组件的用法
a. 自定义一个form类
b. 生成一个form类的实例对象
c. 在前端页面
<form action-"/reg2/" method ="post" novalidate>
{% csrf_token %}
{{ form_obj.as_p }}
{{ form_obj.errors }}
<input type="submit" value="注册">
</form>
form_obj.as_p --> 用p标签包裹每一个字段
#1 as_p 中包括提示性的文本{{ form_obj.username.label }}、input标签{{ form_obj.username }}、响应的错误提示信息{{ form_obj.username.errors.0 }}
#2 不推荐使用as_p,不够灵活; 可以使用以下形式,使后续处理(bootstrap等)更加便利
# 推荐使用以下方式
<div>
<label for="">{{ form_obj.username.label }}</label>
{{ form_obj.username }}
<span class="error">{{ form_obj.username.errors.0 }}</span>
# 根据索引,在errors列表中取第一个error
</div>
form_obj.errors --> 包括全部字段的错误提示
d. 在后端
form_obj.is_valid() --> 对数据做有效性校验
form_obj.cleaned_data --> 获取所有经过校验的数据;直接帮忙过滤掉csrf_token
e. 不让浏览器帮忙校验
<form action="/reg2/" method="post" novalidate>
f. error_messages
error_messages={
"required":"不能为空",
"invalid":"格式错误", # 格式错误,一般用在email中
"min_length":"用户名最短8位"
}
g. 密码
from django import forms
class LoginForm(forms.Form):
...
pwd = forms.CharField(
min_length=6,
label="密码",
widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
# render_value=True: 当提交的密码不符合要求时,原来的值还在
# 一般来说,不推荐使用;防止该用户忘记之前的输入,提高用户体验
)
h. 单选
gender = forms.fields.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
# choices=((value, 提示信息),()),
label="性别",
initial=3, # 默认选中
# 指定生成HTML标签的时候生成的是什么类型的
widget=forms.widgets.RadioSelect # radioSelect
widget=forms.widgets.Select # 单选Select
widget=forms.widgets.CheckboxInput # 单选checkbox
)
i. 多选
hobby = forms.fields.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=[1, 3], #为列表
widget=forms.widgets.SelectMultiple # 多选Select
widget=forms.widgets.CheckboxSelectMultiple # 多选checkbox
)
j. 关于choice的注意事项
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
# 效果:不需重启,choice的选项根据数据库的数据实时更新
from django.forms import Form
from django.forms import widgets
from django.forms import fields
class MyForm(Form):
user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),), # 取代静态生成
initial=2,
widget=widgets.Select
)
def __init__(self, *args, **kwargs):
super(MyForm,self).__init__(*args, **kwargs)
self.fields['user'].choices = models.User.objects.all().values_list('id','caption')
3. 推荐前后端都进行校验
a. 推荐设置前端校验(如校验是否所有必须项都被filled),可以减少后端压力
b. 但是,要以后端校验为主;一来,是对要写入数据库的数据做最终的校验,二来,当前端校验被跳过时(浏览器可禁用JS),后端校验为数据安全性提供保障
4. 自定义校验的三种方式
# 复习正则
import re
r = re.compile(r'^[^键盘膜s]+$') # 不能包含键盘膜和空格
print(r.match('alex键盘膜asd')) # None
# Django框架中用的是match匹配(从头)
print(r.match('alex键盘膜')) # None
print(r.match('键盘膜asd')) # None
print(r.match('alex')) # <_sre.SRE_Match object; span=(0, 4), match='alex'>
1. 正则
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
class RegForm(forms.Form):
username = forms.CharField(
min_length=6,
label="用户名",
error_messages={
"required": "不能为空",
"min_length": "用户名最少6位"
},
validators=[RegexValidator(r'^[^键盘膜s]+$', "用户名不符合社会主义核心价值观!"), ]
2. 自己写函数,注册到validations
import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError
def zhangzhao(value):
print(value)
if "键盘膜" in value:
raise ValidationError("不符合社会主义核心价值观!")
class RegForm(forms.Form):
username = forms.CharField(
min_length=6,
label="用户名",
error_messages={
"required": "不能为空",
"min_length": "用户名最少6位"
},
validators=[zhangzhao, ]
3. 钩子函数
# 局部钩子函数
import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError
class RegForm(forms.Form):
username = forms.CharField(
min_length=6,
label="用户名",
error_messages={
"required": "不能为空",
"min_length": "用户名最少6位"
},
def clean_username(self):
value = self.cleaned_data.get("username")
if "键盘膜" in value:
raise ValidationError("不符合社会主义核心价值观!")
# 要用django中的钩子,需要按照它的规则调用接口;此处必须raise ValidationError
else:
return value
# 要用django中的钩子,需要按照它的规则调用接口;此处必须要有返回值
5. 局部钩子和全部钩子(走源码)
# 根据源码, 校验时,先走设置的校验,再走自定义校验(钩子)
self.errors --> self._errors = {} --> 用来存放错误信息
self.cleaned_data = {} --> 用来存放通过校验的数据
models.User.objects.create(**form_obj.cleaned_data)(清理数据后,添加到数据库)
# 此种方式大大简化了实例化的代码量,以后会经常使用
a. 局部钩子:局部钩子函数主要用来自定义校验某个字段,例如我需要校验用户输入的手机号是否满足一定规则
b. 全局钩子:全局钩子函数主要用来校验所有的字段
全局钩子应用(对比两次密码是否输入一致)
# 用局部钩子解决不了,因为局部钩子在执行中只在本字段(re_pwd)内for循环,没有办法和其他字段(pwd)做对比
class RegForm(forms.Form):
pwd = forms.CharField(
min_length=3,
label="密码",
error_messages={
"required": "不能为空",
"min_length": "密码最少3位"
},
widget=forms.widgets.PasswordInput(
attrs={'class': 'shs1'},
render_value=True
)
)
re_pwd = forms.CharField(
min_length=3,
label="密码",
error_messages={
"required": "不能为空",
"min_length": "密码最少3位"
},
widget=forms.widgets.PasswordInput(
attrs={'class': 'shs1'},
render_value=True
)
)
def clean(self):
pwd = self.cleaned_data.get("pwd")
re_pwd = self.cleaned_data.get("re_pwd")
if pwd != re_pwd:
self.add_error("re_pwd", "两次输入的密码不一致!")
raise ValidationError("两次输入的密码不一致!")
# 要用django中的钩子,需要按照它的规则调用接口;此处必须raise ValidationError
else:
return self.cleaned_data
# 要用django中的钩子,需要按照它的规则调用接口;此处必须要有返回值