一、forms源码解析
# from组件的切入点是is_valid()
def is_valid(self):
"""
Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False.
"""
return self.is_bound and not self.errors
# 如果is_valid要返回True的话 那么self.is_bound要为True self.errors要为Flase
self.is_bound = data is not None or files is not None
# 可以发现is_bound只要传值了就是true,所以直接看errors就行了
@property
def errors(self):
"Returns an ErrorDict for the data provided for the form"
if self._errors is None:
self.full_clean()
return self._errors
self._errors = None
# _errors在定义的时候默认为None,所以这里必定会执行full_clean
def full_clean(self):
"""
Cleans all of self.data and populates self._errors and
self.cleaned_data.
"""
# 定义一个存放错误信息的对象
self._errors = ErrorDict()
# is_bound 只要传值了就不为空
if not self.is_bound: # Stop further processing.
return
# 定义一个存放正确数据的字典
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
# empty_permittedm默认为空,所以这里不会执行
if self.empty_permitted and not self.has_changed():
return
# 这三条是form组件的核心
self._clean_fields() # 内部是校验数据和局部钩子
self._clean_form() # 内部是全局钩子
self._post_clean()
二、cookie与session发展史
Http请求是无状态请求,但是这种无状态的方式会让很多操作变的非常不便,比如说一个需要登录才能使用的网站,无状态的特性会让这个网站的使用处处受限。
之后便出现了一些技术可以在用户第一次登录后把用户名和密码返回给浏览器,让用户浏览器保存在本地,之后访问网站的时候浏览器自动发送这个账号和密码,服务端对其验证。
但是这样的验证方式有很大的安全隐患。
之后便出现了更高级的技术,当用户登录成功后,服务端产生一个随机字符串(存在服务端中,kv键值对形式),交给客户端浏览器保存,之后访问服务端的时候,客户端都会携带这个随机字符串,从而比对用户信息。
但是如果随机字符串被截取到了,还是可以被他人冒充用户,安全隐患依然存在。
在这样不断为了安全更新技术的时代下出现了cookie和session等保存信息的技术
- cookie
- 服务端保存在客户端浏览器上的信息都可以称之为cookie,多个kv键值对形式,客户端下次在访问服务端的时候会带上这个信息,以便服务端提取
- session
- 数据是保存在服务端的,安全性更高,且支持更多字节,且表现形式也是kv键值对,session的实现还是需要cookie作为桥接,要给每个客户端设置唯一id,以cookie记录,这样服务端就知道是谁来访问。
- token
- 为了解决删除数据量过大,在给浏览器保存数据之前先对数据信息加密处理
- 浏览器在访问服务端的时候会携带加密数据,服务端判断加密格式是否是自己产生的
总结:
cookie就是保存在客户端上的信息
session就是保存在服务端上的信息
session是基于cookie工作的,因为Http无状态的特性,总要有一点标识。
三、cookie操作
ps:有时候出现浏览器什么登陆都无法进行的时候,可能是浏览器设置内选择禁止了保存cookie的选项
# 在django中操作cookie就是在操作视图函数的三板斧对象:HttpResponse,redirect,render
# 设置cookie
obj = render()
obj.set_cookie(key,value)
# 设置的时候可以额外添加参数
obj.set_cookie(key,value,max_age=3,expires=3)
# max_age和expires都是设置超时时间,且以秒为单位,不过后者是针对IE浏览器
# 获取cookie
request.COOKIES.get(key)
基于cookie完成登录功能,如果想访问一个页面,没有登录前要跳转到登录页面,登录成功后跳转回要访问的页面,如果直接访问登录页面跳转到home页面
def login(request):
if request.method=='POST':
if request.POST.get('username') == 'hz' and request.POST.get('password') == '123':
# 如果直接访问login 这个结果就是None
next_page = request.GET.get("next")
if next_page:
obj = redirect(next_page)
else:
obj = redirect('/home/')
# 在浏览器中存放cookie数据,以后访问会带着它来
obj.set_cookie('hz','123')
return obj
return HttpResponse('NO')
return render(request,'login.html')
def login_outter(func):
def inner(request,*args,**kwargs):
next_page = request.get_full_path()
if request.COOKIES.get('hz'):
return func(request)
return redirect(f'/login/?next={next_page}')
return inner
@login_outter
def home(request):
return HttpResponse('Home')
@login_outter
def test1(request):
return HttpResponse('test1')
@login_outter
def test2(request):
return HttpResponse('test2')
四、session操作
session数据是保存在服务端的数据库中的,django中这个数据有单独的表django_session表来保存session数据
如果没有找到那就是还没执行数据库迁移命令
django默认的session的过期时间是14天,可以修改
# 设置session
request.session['key'] = value
# 获取session
request.session.get('key')
# 设置过期时间
# 这里的参数可以放整数:多少秒,日期对象:指定日期就失效
request.session.set_expiry()
# 清楚session
request.session.delete() # 只删除服务端的,客户端的不删
request.session.flush() # 都清空,推荐使用
session数据是保存在服务端的,位置可以有多重选择:MySQL,文件,redis,memcache
django_session中的数据条数是取决于浏览器的,同一个ip同一个浏览器只会产生一条数据,如果有多个session对应一个浏览器,可能是之前session过期了还没删
request.session['name'] = 'hz'
这一条代码内部发生了三件事情:
- django内部帮我们生成一个随机字符串(给浏览器的一个cookie数据)
- django内部帮我们把随机字符串和对应的数据存档django_session表中
- 现在内存中产生操作数据的缓存
- 在响应通过django中间件的时候才是真的操作数据
- 将产生的随机字符串返回给浏览器保存到sessionid
request.session.get('name')
这一条代码内部发生了三件事情
- 自动从浏览器请求获取sessionid对应的随机字符串
- 那着这个字符串去django_session表中去查找数据
- 如果比对上了就取出数据并以字典的形式封装到reques.session中,如果没有就返回None
五、CBV如何添加装饰器
from django.views import View
from django.utils.decorators import method_decorator
"""
CBV中django不建议你直接给类的方法加装饰器
无论该装饰器能都正常给你 都不建议直接加
"""
# @method_decorator(login_auth,name='get') # 方式2(可以添加多个针对不同的方法加不同的装饰器)
# @method_decorator(login_auth,name='post')
class MyLogin(View):
@method_decorator(login_auth) # 方式3:它会直接作用于当前类里面的所有的方法
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
# @method_decorator(login_auth) # 方式1:指名道姓
def get(self,request):
return HttpResponse("get请求")
def post(self,request):
return HttpResponse('post请求')