• 21.django中间件源码阅读


    回顾:

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

    # 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表中的数据来说,每一个浏览器是一个用户,而不是你的账号。

  • 相关阅读:
    【整理】数组面试题集锦
    【整理】二叉树面试题集锦
    【转】C++怎么设计只能在堆或者栈分配空间的类以及定义一个不能被继承的类
    【转】面试题:最长回文子串
    【转】后缀数组求最长重复子串
    【转】linux硬链接与软链接
    【转】随机函数面试题
    【转】C++ 重载、覆盖和隐藏
    分类算法评估指标
    Pandas_对某列的内容分列
  • 原文地址:https://www.cnblogs.com/liuzhanghao/p/12030602.html
Copyright © 2020-2023  润新知