回顾:

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

# 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. Xshell的使用以及常用命令

    工具/原料 Xshell 方法/步骤 打开软件,点击新建,在主机哪里写入要访问的ip地址,名称随意 点击文件之后,再点击打开: 就可以看到刚才新建的会话了: 点击连接,就会显示下面的画面,输入用户名, ...

  2. "alert(1) to win" writeup

    地址:http://escape.alf.nu/ level 0: 注意补全,");alert(1)// level 1: 通过添加反斜线使用来转义的反斜线变为字符,\");ale ...

  3. [转帖]Oracle 数据库官方不支持VMWare

    Oracle 数据库官方不支持VMWare [日期:2014-05-13] 来源:Linux社区  作者:myhuaer [字体:大 中 小]   https://www.linuxidc.com/L ...

  4. NOIP2012 D2T3 疫情控制 题解

    题面 这道题由于问最大值最小,所以很容易想到二分,但怎么验证并且如何实现是这道题的难点: 首先我们考虑,对于一个军队,尽可能的往根节点走(但一定不到)是最优的: 判断一个军队最远走到哪可以树上倍增来实 ...

  5. 多进程-Pool进程池

    from multiprocessing import Pool import os,time def Foo(i): time.sleep(2) print("in process&quo ...

  6. 从0开始入门ssm-crm系统实战

    喜欢就点个赞呗! GitHub项目ssm-learn-crm show me the code and take to me,做的出来更要说的明白 1.1 克隆 git clone https://g ...

  7. Git提交代码的小知识

    1.需要切换到项目目录下并创建一个Repository用于提交代码到这个仓库里 cd /g/....//cd后面有空格,用于进入某个项目文件夹 git init//用于创建Repository 2.添 ...

  8. MySQL存储引擎MyISAM和InnoDB有哪些区别?

    一.MyISAM和InnoDB的区别有哪些? 1.InnoDB支持事务,MyISAM不支持.对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在be ...

  9. execjs执行js代码报错:Exception in thread Thread-1

    最近在爬一个js数据加密的网站的时候,出了点问题,困扰了我两天 直接运行js文件的时候正常,但是用execjs运行js代码的时候总是会报错 最后翻了很多博客之后,终于找到了原因:原因是有一个程序在使用 ...

  10. spring基于注解的IoC以及IoC的案例

    1.Spring中IoC的常用注解 1.1明确: (1)基于注解的配置和xml的配置要实现的功能都是一样的,都是要降低程序之间的耦合,只是配置的形式不一样 2.案例:使用xml方式和注解方式实现单表的 ...