回顾:

关于里面的源码流程大家可以全看视频,因为代码的跳动性很大,而且会多次调用通过一方法,所以关于中间源码的部分去找个视频看一看,我写的不是很清楚。

# 1 cookie session

# 2 forms, modelsform
(1)效验数据
class Book(model.Model):
name = models.CharField(min_length=5)
price = models.InterField()
publish = models.ForeignKey("Pusblish")
authors = models.Many2Many("author") class BookForm(forms.Form):
name = models.Charfield(min_length=10)
price = models.Intergerfield()
publish = forms.ModelChoiceField(queryset=Publish.object.all())
authors = forms.ModelMultipleChoiceField(queryset=Publish.object.all())
bf = BookForm({"name": "alex", "price": 100})
if bf.is_valid():
bf.cleaned_data:{}
else:
bf.cleaned_data:{}
bf.error:{}
(2)渲染标签
bf = BookForm()
三种渲染方式
-- 1
{{bf.as_p}}
-- 2
{{bf.name}}
-- 3
{%for field in bf%}
{{field}}
{%endfor%}
(3) 重置数据
(4) 局部钩子函数
(5) 全局钩子
modelsform:
class Book(model.Model):
name = models.CharField(min_length=5)
price = models.InterField()
publish = models.ForeignKey("Pusblish")
authors = models.Many2Many("author")
class BookModelForm():
class Meta:
model=Book
fields="__all__"
# 添加记录
bf = BookForm(data=request.data)
if bf.is_valid():
bf.save() # create
else:
bf.cleaned_data:{}
bf.error:{}
# 编辑记录
bf = BookForm(data=request.data,instance=instance_obj)
if bf.is_valid():
bf.save() # update
else:
bf.cleaned_data:{}
bf.error:{}

3 中间件

## 3 中间件
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
- SessionMiddleware中间件源码流程
request.session["user_id"] = 12
- 生成随机字符串
- 加入session表中
- 设置进入set_cookied("")
1.可以看到这一步里面其实只有赋值这一步操作request.session=....,具体这个SessionStore是什么下面就是关键的地方
"""
def __init__(self, get_response=None):
self.get_response = get_response
engine = import_module(settings.SESSION_ENGINE)
self.SessionStore = engine.SessionStore def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key) _session=self._session_cache
"""
2.设置session:request.session["user_id"] = 12
进入SessionStore类中,字典样式的赋值我们找他的__setitem__方法,发现里面有复制操作,但是self._session是什么?看2.1
def __setitem__(self, key, value):
self._session[key] = value # 将self._session["user_id"] = 12
self.modified = True # 同时将更改的标志改为true,然后赋值到这里完毕,
# 9 但是如果同时进行两次赋值呢,跟着上次的逻辑向下读就好了。你会发现2.1里面的try下面第一句,这个时候因为第一次的赋值,所以self._session_cache有值了,他就之久返回给了self._session,进行二次赋值。 # 10 那如果这个时候再访问另一个设置session的接口的呢,因为每一次的请求都是没有关系的,假如第二个接口request.session["name"] = "youda",那么问题就来了,和第一次一样这个请求里面self._session_cache中还是没有值,但是self.session_key因为第一次的访问我们发送给浏览器了,它吧cookie给带回来了,而session就存放在cookie中,而在实例化SessionStore的时候session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME),就是获取到的上一次设置的session的key。这个时候就会走到2.1中的self._session_cache = self.load()这个方法中,直接告诉你拿到了什么。就是你数据库中,这个session_key对应的session_data,然后返回给self._session # 3 之后在base里面看到这一个
_session = property(_get_session)
_get_session中发生了什么呢?看一下
def _get_session(self, no_load=False):
self.accessed = True # 先把是否访问过设置为true
try:
# 4.之后尝试返回self._session_cache,但是找了一圈都没有地方出现,然后发现下面异常捕获中进行了处理
return self._session_cache
except AttributeError:
# 5.先判断self.session_key是否为None,self.session_key是什么看下面,
# 11第十一步走到这里发现self.session_key有值,走else分支
if self.session_key is None or no_load:
# 6.然后这里返回了一个字典,然后发现self._session=self._session_cache={},上去赋值那里
self._session_cache = {}
else:
# 12 self.load()里面就是去数据库session表中中,这个session_key对应的session_data,然后返回给self._session,可以自己点进入看看逻辑很简单
self._session_cache = self.load()
return self._session_cache
# 7 继续吧self.session_key,走到_get_session_key,第一次self.__session_key=None
session_key = property(_get_session_key)
def _get_session_key(self):
return self.__session_key 到这里process_request走完了,下来开始process_response 13.获取session设置时间的
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = http_date(expires_time)
14. 这里就是session保存创建的地方以及更新的地方
request.session.save()
def save(self, must_create=False):
if self.session_key is None: # 第一次进来肯定没有值所以走create方法
return self.create()
15.我们看看create方法干了什么
def create(self):
while True:
# 其实就是获取一个随机字符串,同时判断这个key是否已存在,这个时候就是给self._session_key赋值的过程,触发propory的_set_session_key方法
self._session_key = self._get_new_session_key()
try:
# 这个时候有session_key了我们走save的那个方法
self.save(must_create=True)
except CreateError:
continue
self.modified = True
return
16.save() 第二次的时候走这里的时候
def save(self, must_create=False):
if self.session_key is None:
return self.create() # must_create在走create的时候已经改为了true
# 这个方法中其实就是返回self._session_cache,以为有self.session_key(从cookie中获取的),所以这里走的是呢个self._session_cache = self.load()
data = self._get_session(no_load=must_create)
# 17 使用session表创建这个对象
obj = self.create_model_instance(data)
# 获取到底是用那个数据库
using = router.db_for_write(self.model, instance=obj)
try:
with transaction.atomic(using=using):
# 18 然后这里插入数据库,至于里面的更新还是创建就是看走不走self.create()方法
obj.save(force_insert=must_create, force_update=not must_create, using=using)
except IntegrityError:
if must_create:
raise CreateError
raise
except DatabaseError:
if not must_create:
raise UpdateError
raise
19.这里数据已经写入数据库了接下里干什么
# 这里就是将{session_id: self.session_key}添加进入cookie,
response.set_cookie(
settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None,
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
samesite=settings.SESSION_COOKIE_SAMESITE,
)
request.session.get("user_id")

特别注意:

假如有两次方法为,第一次访问的时候设置了一个self.cache在这次的请求中,第二次访问的时候这个属性还在不在,答案是不在,http请求无状态,无连接,每次访问都是一次新的请求,里面有什么和你第一次里面没有必然的关系,关键看你传什么。

session的bug

​ 关于Django中的session的问题,如果我们直接时候request.session给每一个用户设置session键和值,加入第一个人登录设置了user_id和user_name,两个键,但是在其他的地方,他访问reset_phone接口的时候,又设置了一个session键是phone,这个时候(session一共有三个键)他出去了,电脑没有关闭,它的同学使用他的电脑登陆了自己的账号(他们登录的是同一个app),这个时候发生了什么,想一想,上一个人的session还在,他登陆了,这个时候login接口因为他们设置的key懂事user_id和user_name而被更新了,但是phone那个键还在,表示这个人可以拿到上一个人的信息,如果他phone里面存的不是电话而是银行卡密码,那么第一个人的信息泄露了,第一个人钱丢了谁负责。所以我们在项目中关键性的信息不要直接使用session来存储,或者我们可以在第二个人点击登陆的时候,判断session里面是否有值,如果有我们先清空(清空操作上是清楚数据库session表中的数据)在设置。

​ 而上面说每次请求都不一样,问什么第二人能获取第一个人的数据,是因为,如果没有清空session,那么session里面会有一步的判断,如果使用同一个浏览器,session表中存储的key是相同的,他发现有这个key那么就不回去重新创建一个key,而是直接更新这个key对应的data,更新的时候他他会重数据库拿到上一次的,然后再把这次的数据更新,之后就会出现数据泄露的问题。

​ 其中对于session表中的数据来说,每一个浏览器是一个用户,而不是你的账号。

21.django中间件源码阅读的更多相关文章

  1. 重新整理 .net core 实践篇——— 权限中间件源码阅读[四十六]

    前言 前面介绍了认证中间件,下面看一下授权中间件. 正文 app.UseAuthorization(); 授权中间件是这个,前面我们提及到认证中间件并不会让整个中间件停止. 认证中间件就两个作用,我们 ...

  2. Python 实现接口类的两种方式+邮件提醒+动态导入模块+反射(参考Django中间件源码)

    实现接口类的两种方式 方式一 from abc import ABCMeta from abc import abstractmethod class BaseMessage(metaclass=AB ...

  3. Python 实现抽象类的两种方式+邮件提醒+动态导入模块+反射(参考Django中间件源码)

    实现抽象类的两种方式 方式一 from abc import ABCMeta from abc import abstractmethod class BaseMessage(metaclass=AB ...

  4. Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块

    目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax c ...

  5. 21 BasicTaskScheduler基本任务调度器(一)——Live555源码阅读(一)任务调度相关类

    21_BasicTaskScheduler基本任务调度器(一)——Live555源码阅读(一)任务调度相关类 BasicTaskScheduler基本任务调度器 BasicTaskScheduler基 ...

  6. PHP源码阅读笔记一(explode和implode函数分析)

    PHP源码阅读笔记一一.explode和implode函数array explode ( string separator, string string [, int limit] )此函数返回由字符 ...

  7. kubernetes源码阅读及编译

    kubernetes源码阅读 工欲善其事,必先利其器.在阅读kubernetes源码时,我也先后使用过多个IDE,最终还是停留在IDEA上. 我惯用的是pycharm(IDEA的python IDE版 ...

  8. Spark源码阅读之存储体系--存储体系概述与shuffle服务

    一.概述 根据<深入理解Spark:核心思想与源码分析>一书,结合最新的spark源代码master分支进行源码阅读,对新版本的代码加上自己的一些理解,如有错误,希望指出. 1.块管理器B ...

  9. 《java.util.concurrent 包源码阅读》 结束语

    <java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...

随机推荐

  1. Job for jenkins.service failed because the control process exited with error code.

    root@xiakaibi-PC:~# service jenkins restartJob for jenkins.service failed because the control proces ...

  2. springboot整合springdatajpa时jar冲突

    1.springboot整合springdatajpa测试时报No bean named 'entityManagerFactory' available错误 2.运行springboot主程序时报以 ...

  3. PreparedStatement 以及事务的注意事项

    a).PreparedStatement 可以进行批量操作,但是与Statement有一定的区别 1. Statement可以进行不同sql语句的批量操作 即可以同时进行 crud 操作. Strin ...

  4. PHP,mysql,nginxunx中安装

    一:安装PHP,mysql,nginx linux装软件方式: 1.源码安装:下载wget-->解压tar -zxvf -->配置 ./configure --->编译make -- ...

  5. Ansible 系统概述与部署

    Ansible 系统概述 Ansible是一款为类Unix系统开发的自由开源的配置和自动化工具.它用Python写成,类似于saltstack和Puppet但是有一个不同和优点是我们不需要在节点中安装 ...

  6. Thinkphp5.0上传图片与运行python脚本

    这里只体现了php可以通过批处理文件调用python脚本的效果 控制器代码 访问路径为127.0.0.1/index/index/upload. index模块,index控制器,upload方法. ...

  7. postgresql 服务无法启动,日志中报如下错误

    1.postgresql 服务无法启动,日志中报如下错误,磁盘空间足够. 无法找到来自源 PostgreSQL 的事件 ID 0 的描述.本地计算机上未安装引发此事件的组件,或者安装已损坏.可以安装或 ...

  8. linux下vim常用命令 (更新中...)

    1.注释多行 1). 首先按esc进入命令行模式下,按下Ctrl + v,进入VISUAL BLOCK模式; 2). 在行首使用上下键选择需要注释的多行; 3). 按下键盘(大写)“I”键,进入插入模 ...

  9. CF1151F Sonya and Informatics

    cf luogu 我们最终要的序列一定是前面全是0,后面全是1,假设总共\(m\)个0,那么这等价于前\(m\)位0的个数为\(m\).当然一开始可能数量没有\(m\) 那就把前\(m\)位0的数量作 ...

  10. less 经典范例 bootstrap 的 less 版本 常用 less 代码

    1. bootstrap 的 less 版本 2.less 文件分布 /*! * Bootstrap v3.3.7 (http://getbootstrap.com) * Copyright 2011 ...