Django之Form、跨站请求以及cookie、session
Form表单
常规html页面的form表单验证
常规页面中,如果想实现对表单中用户输入信息的数据验证,需要配合Ajax来实现.
使用前我们先来熟悉下函数参数:request,其中包含的意义:
request.path 除去了域名和端口的访问路径,
request.get_host 域名+端口信息
request.get_full_path() 所有路径,包含传递的参数
requets.is_secure() 是否使用https进行链接
Django中的form库:每一个邦定Form实体都有一个errors属性,它为你提供了一个字段与错误消息相映射的字典表。
可以这么定义:
>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f.errors
{'message': [u'This field is required.']}
实现验证的必要条件:
- 在views.py中定义一个继承了父类
forms.Form
的子类 - 在页面中需要设置
name
属性 - 后台验证时使用LoginForm(request.POST)获取表单确认结果
- 获取结果后,使用LoginForm(request.POST).is_valid()判断是否有误(True为无错误,False为有错误)
- 用户页面提交时,需要将输入内容拼接为字典形式,并且属性name的值要与views.py 中定义的一致
下面我们看下代码吧:
views.py代码:
class LoginForm(forms.Form):
user = forms.CharField(required=True, error_messages={'required': '用户名不能为空.'})
pwd = forms.CharField(required=True,
min_length=6,
max_length=10,
error_messages={'required': '密码不能为空.', 'min_length': "至少6位"})
num = forms.IntegerField(error_messages={'required': '数字不能空.', 'invalid': '必须输入数字'})
phone = forms.CharField(validators=[mobile_validate, ], )
def login1(request):
if request.method == 'POST':
result = {'status': False, 'message': None}
obj = LoginForm(request.POST)
ret = obj.is_valid()
if ret:
print(obj.clean())
result['status'] = True
else:
from django.forms.utils import ErrorDict
print(type(obj.errors), obj.errors.as_json())
# print(obj.errors)
error_str = obj.errors.as_json()
result['message'] = json.loads(error_str)
return HttpResponse(json.dumps(result))
return render(request, 'login1.html')
login1.html页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.error_msg{
color: red;
}
</style>
</head>
<body>
<div>
<div>
<input type="text" name="user" />
</div>
<div>
<input type="password" name="pwd" />
</div>
<input type="button" value="提交" onclick="DoSubmit();" />
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function DoSubmit(){
$('.error_msg').remove();
var input_dict = {};
$('input').each(function(){
var v = $(this).val();
var n = $(this).attr('name');
input_dict[n] = v;
});
console.log(input_dict);
$.ajax({
url: '/login1/',
type: 'POST',
data: input_dict,
dataType:'json',
success: function (result) {
if(result.status){
location.href = '/index/'
}else {
$.each(result.message,function (k,v) {
console.log(k,v[0].message);
var tag = document.createElement('span');
tag.className = 'error_msg';
tag.innerText = v[0].message;
$('input[name="'+k +'"]').after(tag);
})
}
}
})
}
</script>
</body>
</html>
怎么样,比较复杂吧,为什么复杂,我们要自己创建便签,自己生成HTML页面,很麻烦,那么我们就来看下django自带的form库!
Django自带form进行验证
权威django讲解form的地址:http://www.kancloud.cn/thinkphp/django-book/39587
Django中的form一般自带两个功能
- 自动生产HTML
- 验证用户输入
views.py设置:
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误')
class LoginForm(forms.Form):
user = forms.CharField(required=True, error_messages={'required': '用户名不能为空.'})
pwd = forms.CharField(required=True,
min_length=6,
max_length=10,
error_messages={'required': '密码不能为空.', 'min_length': "至少6位"})
num = forms.IntegerField(error_messages={'required': '数字不能空.', 'invalid': '必须输入数字'})
phone = forms.CharField(validators=[mobile_validate, ], )
topic = forms.ChoiceField(choices=TOPIC_CHOICES,label='选择评分')
###页面处理
def login(request):
if request.POST:
objPost = LoginForm(request.POST)
ret = objPost.is_valid()
if ret:
print(objPost.clean())
else:
from django.forms.utils import ErrorDict
# print(type(obj.errors),obj.errors.as_json())
# obj1.errors
pass
return render(request, 'login.html', {'obj1': objPost})
else:
objGet = LoginForm()
return render(request, 'login.html', {'obj1': objGet})
其中error_messages定义了出错时显示在页面的错误消息
此处的topic中的choices需要在models.py中定义一个数组.
TOPIC_CHOICES = (
('leve1', '差评'),
('leve2', '中评'),
('leve3', '好评'),
)
html的页面中就很简单了,我们来看下:
login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.error_msg{
color: red;
}
</style>
</head>
<body>
<form action="/login/" method="POST">
<div>
{{ obj1.user }}
{% if obj1.errors.user %}
<span class="error_msg">{{ obj1.errors.user.0 }}</span>
{% endif %}
</div>
<div>
{{ obj1.pwd }}
{% if obj1.errors.pwd %}
<span class="error_msg">{{ obj1.errors.pwd.0 }}</span>
{% endif %}
</div>
<div>
{{ obj1.num }}
{% if obj1.errors.num %}
<span class="error_msg">{{ obj1.errors.num.0 }}</span>
{% endif %}
</div>
<div>
{{ obj1.phone }}
{% if obj1.errors.phone %}
<span class="error_msg">{{ obj1.errors.phone.0 }}</span>
{% endif %}
</div>
<input type="submit" value="提交"/>
</form>
</body>
</html>
代码很少就能完美解决,真方便...
自定义form验证机制
关于自定义Form验证机制,可以看一下这个:http://www.cnblogs.com/ccorz/p/5868380.html
跨站请求csrf
概述
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware
来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。
- 全局:文件位置
setting.py
,文件中间件位置:django.middleware.csrf.CsrfViewMiddleware
- 局部:
-
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
-
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
-
注意:以上需要导入模块:from
django.views.decorators.csrf
import csrf_exempt,csrf_protect
-
应用
使用时,views.py中无需设置任何东西,常规return render(xxx)
即可,主要是在HTML页面中.
普通表单
普通form中添加在form中设置{% csrf_token %}
即可.如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>csrf</title>
</head>
<body>
<form action="/csrf/" method="POST">
{% csrf_token %}
<input type="text" name="v"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
Ajax
对于传统的form,可以通过表单的方式将token再次发送到服务端,而对于ajax的话,使用如下方式。
需要在script部分写入如下内容:
var csrftoken = $.cookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
我们可以将以上代码写入一个js文件,然后在使用时导入script.
html如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>csrf</title>
</head>
<body>
<input type="button" value="ajax提交" onclick="doajax();" />
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script src="/static/csrf.js"></script>
<script>
{# var csrftoken = $.cookie('csrftoken');#}
{##}
{# function csrfSafeMethod(method) {#}
{# // these HTTP methods do not require CSRF protection#}
{# return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));#}
{# }#}
{# $.ajaxSetup({#}
{# beforeSend: function(xhr, settings) {#}
{# if (!csrfSafeMethod(settings.type) && !this.crossDomain) {#}
{# xhr.setRequestHeader("X-CSRFToken", csrftoken);#}
{# }#}
{# }#}
{# });#}
function doajax() {
$.ajax({
url:'/csrf/',
type:'POST',
data:{'k1':'v1'},
success:function (data) {
console.log(data)
}
})
}
</script>
</body>
</html>
cookie
获取cookie
request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
参数:
default: 默认值
salt: 加密盐
max_age: 后台控制过期时间
设置Cookie
rep = HttpResponse(...) 或 rep = render(request, ...)
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)
参数:
key, 键
value='', 值
max_age=None, 超时时间
expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
path='/', Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
domain=None, Cookie生效的域名
secure=False, https传输
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
前端操作cookie
由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。
<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });
session
session一般是与cookie配合一起使用的,否则没有cookie的session没有什么实际意义.
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:
- 数据库(默认)
- 缓存
- 文件
- 缓存+数据库
- 加密cookie
数据库session
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
b. 使用
def index(request):
# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']
# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()
# 用户session的随机字符串
request.session.session_key
# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()
# 检查 用户session的随机字符串 在数据库中是否
request.session.exists("session_key")
# 删除当前用户的所有Session数据
request.session.delete("session_key")
...
缓存session
配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
文件session
配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
缓存+数据库session
数据库用于做持久化,缓存用于提高效率
配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
加密cookie session
配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
使用方式都一样.
扩展:session用户验证
def session_login(request):
if request.method == 'POST':
u = request.POST.get('user', None)
p = request.POST.get('pwd', None)
if p == '123' and u in USER_LIST:
request.session['user'] = u
return redirect('/session_index/')
return render(request, 'session_login.html')
session 实例:用户登录退出
分析需求,一共三个页面,登录、登录后提示成功页面、以及注销页面.
views.py设置:
USER_LIST = ['alex', 'hu', 'eric', 'cc']
#设置装饰器
def check_login(func):
def inner(request, *args, **kwargs):
user = request.session.get('user')
if not user:
return redirect('/session_login')
return func(request, *args, **kwargs)
return inner
#设置登录页面后台处理
def session_login(request):
if request.method == 'POST':
u = request.POST.get('user', None)
p = request.POST.get('pwd', None)
if p == '123' and u in USER_LIST:
request.session['user'] = u
return redirect('/session_index/')
return render(request, 'session_login.html')
#登录后的提示页面
@check_login
def session_index(request):
user = request.session.get('user', None)
return render(request, 'session_index.html', {'user': user})
#注销后台处理
@check_login
def session_logout(request):
del request.session['user']
return redirect('/session_login/')
登录页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/session_login/" method="POST">
<input type="text" name="user">
<input type="text" name="pwd">
<input type="submit" value="提交" />
</form>
</body>
</html>
登录后的跳转首页:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>欢迎:{{ user }}登录</h1>
<a href="/session_logout/">注销</a>
</body>
</html>