Django框架——forms组件、cookie与session
1 forms 组件
1 校验字段功能
针对一个实例:注册用户讲解。
模型:models.py
class UserInfo(models.Model):
name=models.CharField(max_length=32)
pwd=models.CharField(max_length=32)
email=models.EmailField(
模版文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<div>
<label for="user">用户名</label>
<p><input type="text" name="name" id="name"></p>
</div>
<div>
<label for="pwd">密码</label>
<p><input type="password" name="pwd" id="pwd"></p>
</div>
<div>
<label for="r_pwd">确认密码</label>
<p><input type="password" name="r_pwd" id="r_pwd"></p>
</div>
<div>
<label for="email">邮箱</label>
<p><input type="text" name="email" id="email"></p>
</div>
<input type="submit">
</form>
</body>
</html>
视图函数:
# forms组件
from django.forms import widgets
wid_01=widgets.TextInput(attrs={"class":"form-control"})
wid_02=widgets.PasswordInput(attrs={"class":"form-control"})
class UserForm(forms.Form):
name=forms.CharField(max_length=32,
widget=wid_01
)
pwd=forms.CharField(max_length=32,widget=wid_02)
r_pwd=forms.CharField(max_length=32,widget=wid_02)
email=forms.EmailField(widget=wid_01)
tel=forms.CharField(max_length=32,widget=wid_01)
def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干净的字段以及对应的值
else:
print(form.cleaned_data) #
print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]}
print(form.errors.get("name")) # ErrorList ["错误信息",]
return HttpResponse("OK")
form=UserForm()
return render(request,"register.html",locals())
2 渲染标签功能
渲染方式1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form action="" method="post">
{% csrf_token %}
<div>
<label for="">用户名</label>
{{ form.name }}
</div>
<div>
<label for="">密码</label>
{{ form.pwd }}
</div>
<div>
<label for="">确认密码</label>
{{ form.r_pwd }}
</div>
<div>
<label for=""> 邮箱</label>
{{ form.email }}
</div>
<input type="submit" class="btn btn-default pull-right">
</form>
</div>
</div>
</div>
</body>
</html>
渲染方式2
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<input type="submit" class="btn btn-default pull-right">
</form>
渲染方式3
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-default pull-right">
</form>
3 渲染错误信息功能
视图
def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干净的字段以及对应的值
else:
print(form.cleaned_data) #
print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]}
print(form.errors.get("name")) # ErrorList ["错误信息",]
return render(request,"register.html",locals())
form=UserForm()
return render(request,"register.html",locals())
模板
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }} <span class="pull-right" style="color: red">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<input type="submit" class="btn btn-default">
</form>
4 组件的参数配置
class Ret(Form):
name = forms.CharField(max_length=10, min_length=2, label='用户名',
error_messages={'required': '该字段不能为空', 'invalid': '格式错误', 'max_length': '太长',
'min_length': '太短'},
widget=widgets.TextInput(attrs={'class':'form-control'}))
pwd = forms.CharField(max_length=10, min_length=2, widget=widgets.PasswordInput(attrs={'class':'form-control'}))
email = forms.EmailField(label='邮箱', error_messages={'required': '该字段不能为空', 'invalid': '格式错误'})
5 局部钩子
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
def clean_name(self):
val=self.cleaned_data.get("name")
ret=UserInfo.objects.filter(name=val)
if not ret:
return val
else:
raise ValidationError("该用户已注册!")
def clean_tel(self):
val=self.cleaned_data.get("tel")
if len(val)==11:
return val
else:
raise ValidationError("手机号格式错误")
6 全局钩子
def clean(self):
pwd=self.cleaned_data.get('pwd')
r_pwd=self.cleaned_data.get('r_pwd')
if pwd and r_pwd:
if pwd==r_pwd:
return self.cleaned_data
else:
raise ValidationError('两次密码不一致')
else:
return self.cleaned_data
pwd_err=my_form.errors.get('__all__')
from django import forms
from django.forms import widgets
from app01.models import UserInfo
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
class UserForm(forms.Form):
name=forms.CharField(min_length=4,label="用户名",error_messages={"required":"该字段不能为空"},
widget=widgets.TextInput(attrs={"class":"form-control"})
)
pwd=forms.CharField(min_length=4,label="密码",
widget=widgets.PasswordInput(attrs={"class":"form-control"})
)
r_pwd=forms.CharField(min_length=4,label="确认密码",error_messages={"required":"该字段不能为空"},widget=widgets.TextInput(attrs={"class":"form-control"}))
email=forms.EmailField(label="邮箱",error_messages={"required":"该字段不能为空","invalid":"格式错误"},widget=widgets.TextInput(attrs={"class":"form-control"}))
tel=forms.CharField(label="手机号",widget=widgets.TextInput(attrs={"class":"form-control"}))
def clean_name(self):
val=self.cleaned_data.get("name")
ret=UserInfo.objects.filter(name=val)
if not ret:
return val
else:
raise ValidationError("该用户已注册!")
def clean_tel(self):
val=self.cleaned_data.get("tel")
if len(val)==11:
return val
else:
raise ValidationError("手机号格式错误")
def clean(self):
pwd=self.cleaned_data.get('pwd')
r_pwd=self.cleaned_data.get('r_pwd')
if pwd and r_pwd:
if pwd==r_pwd:
return self.cleaned_data
else:
raise ValidationError('两次密码不一致')
else:
return self.cleaned_data
from django.shortcuts import render,HttpResponse
from app01.myforms import *
def reg(request):
if request.method=="POST":
print(request.POST)
#form=UserForm({"name":"yu","email":"123@qq.com","xxxx":"alex"})
form=UserForm(request.POST) # form表单的name属性值应该与forms组件字段名称一致
print(form.is_valid()) # 返回布尔值
if form.is_valid():
print(form.cleaned_data) # {"name":"yuan","email":"123@qq.com"}
else:
print(form.cleaned_data) # {"email":"123@qq.com"}
# print(form.errors) # {"name":[".........."]}
# print(type(form.errors)) # ErrorDict
# print(form.errors.get("name"))
# print(type(form.errors.get("name"))) # ErrorList
# print(form.errors.get("name")[0])
# 全局钩子错误
#print("error",form.errors.get("__all__")[0])
errors=form.errors.get("__all__")
return render(request,"reg.html",locals())
'''
form.is_valid() :返回布尔值
form.cleaned_data :{"name":"yuan","email":"123@qq.com"}
form.errors :{"name":[".........."]}
'''
form=UserForm()
return render(request,"reg.html",locals())
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.error{
color: red;
}
</style>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
{#<h3>简单form</h3>#}
{##}
{##}
{#<form action="" method="post" novalidate>#}
{# {% csrf_token %}#}
{# <p>用户名<input type="text" name="name"></p>#}
{# <p>密码 <input type="text" name="pwd"></p>#}
{# <p>确认密码 <input type="text" name="r_pwd"></p>#}
{# <p>邮箱 <input type="text" name="email"></p>#}
{# <p>手机号 <input type="text" name="tel"></p>#}
{# <input type="submit">#}
{##}
{#</form>#}
<hr>
<h3>forms组件渲染方式1</h3>
<form action="" method="post" novalidate>
{% csrf_token %}
<p>{{ form.name.label }}
{{ form.name }} <span class="pull-right error">{{ form.name.errors.0 }}</span>
</p>
<p>{{ form.pwd.label }}
{{ form.pwd }} <span class="pull-right error">{{ form.pwd.errors.0 }}</span>
</p>
<p>确认密码
{{ form.r_pwd }} <span class="pull-right error">{{ form.r_pwd.errors.0 }}</span><span class="pull-right error">{{ errors.0 }}</span>
</p>
<p>邮箱 {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span></p>
<p>手机号 {{ form.tel }} <span class="pull-right error">{{ form.tel.errors.0 }}</span></p>
<input type="submit">
</form>
{#<h3>forms组件渲染方式2</h3>#}
{##}
{#<form action="" method="post" novalidate>#}
{# {% csrf_token %}#}
{##}
{# {% for field in form %}#}
{##}
{# <div>#}
{# <label for="">{{ field.label }}</label>#}
{# {{ field }}#}
{# </div>#}
{##}
{# {% endfor %}#}
{##}
{# <input type="submit">#}
{#</form>#}
{##}
{#<h3>forms组件渲染方式3</h3>#}
{##}
{#<form action="" method="post">#}
{# {% csrf_token %}#}
{##}
{# {{ form.as_p }}#}
{##}
{# <input type="submit">#}
{#</form>#}
</div>
</div>
</div>
</body>
</html>
2 cookie与session
1 会话跟踪技术
什么是会话跟踪
我们需要先了解一下什么是会话!可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应。例如你给10086打个电话,你就是客户端,而10086服务人员就是服务器了。从双方接通电话那一刻起,会话就开始了,到某一方挂断电话表示会话结束。在通话过程中,你会向10086发出多个请求,那么这多个请求都在一个会话中。在Web中,客户向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。
在一个会话的多个请求中共享数据,这就是会话跟踪技术。例如在一个会话中的请求如下: 请求银行主页;
- 请求登录(请求参数是用户名和密码);
- 请求转账(请求参数与转账相关的数据);
- 请求信誉卡还款(请求参数与还款相关的数据)。
在这上会话中当前用户信息必须在这个会话中共享的,因为登录的是张三,那么在转账和还款时一定是相对张三的转账和还款!这就说明我们必须在一个会话过程中有共享数据的能力。
会话路径技术使用Cookie或session完成
我们知道HTTP协议是无状态协议,也就是说每个请求都是独立的!无法记录前一次请求的状态。但HTTP协议中可以使用Cookie来完成会话跟踪!在Web开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。
2 cookie介绍
cookie的由来
大家都知道HTTP协议是无状态的
无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。
一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。
状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。
什么是cookie
其实Cookie是key-value结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!
cookie的原理
cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。
Cookie规范
- Cookie大小上限为4KB;
- 一个服务器最多在客户端浏览器上保存20个Cookie;
- 一个浏览器最多保存300个Cookie;
上面的数据只是HTTP的Cookie规范,但在浏览器大战的今天,一些浏览器为了打败对手,为了展现自己的能力起见,可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现把你硬盘占满的可能!注意,不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器。
Cookie的覆盖
如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie: a=A;第二请求服务器端发送的是:Set-Cookie: a=AA,那么客户端只留下一个Cookie,即:a=AA。
在浏览器中查看cookie
浏览器中按F12,点network—cookies就能看到
Django中操作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, 超时时间 cookie需要延续的时间(以秒为单位)如果参数是\ None`` ,这个cookie会延续到浏览器关闭为止
- expires=None, 超时时间(IE requires expires, so set it if hasn’t been already.)
- path=’/‘, Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。
- domain=None, Cookie生效的域名 你可用这个参数来构造一个跨站cookie。如, domain=”.example.com”所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取
- secure=False, 浏览器将通过HTTPS来回传cookie
- httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
删除Cookie
def logout(request):
rep = redirect("/login/")
rep.delete_cookie("user") # 删除用户浏览器上之前设置的usercookie值
return rep
Cookie版登录校验
def login_auth(func):
def inner(request,*args,**kwargs):
next_url=request.get_full_path()
if request.COOKIES.get('is_login'):
return func(request,*args,**kwargs)
else:
return redirect('cookie_login/?next=%s'%next_url)
return inner
@login_auth
def cookie_order(request):
return HttpResponse('我是订单页面')
@login_auth
def cookie_index(request):
name=request.COOKIES.get('username')
return render(request,'cookie_index.html',{'name':name})
def cookie_login(request):
if request.method =='POST':
next_url=request.GET.get('next')
name=request.POST.get('name')
password=request.POST.get('password')
if name == 'lqz' and password == '123':
import datetime
now=datetime.datetime.now().strftime('%Y-%m-%d %X')
print(now)
obj=redirect(next_url)
obj.set_cookie('is_login',True)
obj.set_cookie('username',name)
obj.set_cookie('login_time',now)
return obj
return render(request, 'cookie_login.html')
Session
Session的由来
Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。
问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。
我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。
总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。
另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。
Django中Session相关方法
# 获取、设置、删除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的key
request.session.session_key
# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()
# 检查会话session的key在数据库中是否存在
request.session.exists("session_key")
# 删除当前会话的所有Session数据(只删数据库)
request.session.delete()
# 删除当前的会话数据并删除会话的Cookie(数据库和cookie都删)。
request.session.flush()
这用于确保前面的会话数据不可以再次被用户的浏览器访问
例如,django.contrib.auth.logout() 函数中就会调用它。
# 设置会话Session和Cookie的超时时间
request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。
Django中使用session时,做的事:
# 生成随机字符串
# 写浏览器cookie -> session_id: 随机字符串
# 写到服务端session:
# {
# "随机字符串": {'user':'alex'}
# }
Django中的Session配置
1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()
4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
其他公用设置项:
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,默认修改之后才保存(默认)
思考,如果第二个人再次再同一个浏览器上登录,django-session表会怎样?
CBV中加装饰器
from django import views
from django.utils.decorators import method_decorator
# @method_decorator(login_auth,name='get')
# @method_decorator(login_auth,name='post')
class UserList(views.View):
# @method_decorator(login_auth)
def dispatch(self, request, *args, **kwargs):
obj=super().dispatch(request, *args, **kwargs)
return obj
@method_decorator(login_auth)
def get(self,request):
return HttpResponse('我是用户列表')
def post(self,request):
return HttpResponse('我是用户列表')
本文来自博客园,作者:喝茶看狗叫,转载请注明原文链接:https://www.cnblogs.com/zdwzdwzdw/p/17487891.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY