Thursday, October 22, 2009

django的session处理

I. cookie与session的关系

HTTP是无状态协议, 它区别请求是借且于HTTP中的cookie字段来实现.
服务器端发出带cookie字段的HTTP response大致如下:
Set-Cookie: name=newvalue; expires=date; path=/; domain=.example.org
客户端发出带cookie字段的HTTP request大致如下:
Cookie: name=newvalue; expires=date; path=/; domain=.example.org

HTTP协议中对cookie有约定的属性, 见: http://en.wikipedia.org/wiki/HTTP_cookie#Cookie_attributes

session是在cookie的机制的基础上, 增加一个可以被WEB服务使用的token. 此token对应于服务器端的一个些具体的, 不希望在HTTP中传输的数据.

这就是{key:vaule}的关系

II. django里的cookie与session

在django的request对象中, 有两个与会话相关的对象:
request.COOKIES: 一个dict实例
request.session. 一个SessionStore类的实例. 在SessionMiddleware中被创建.

django在默认时, cookie被加入了一个叫sessionid的属性. 如下:
(Pdb) print request.COOKIES
{'sessionid': '40c340a947b41e4def92ed70c25affbe'}
* 可以通过settings.py修改

这个属性就是django里与session对应的token. 被称为session_key, 如:
(Pdb) print request.session.session_key
40c340a947b41e4def92ed70c25affbe

III. 服务器端返回set-cookie段

要把session对应的session_key放在HTTP的cookie段中, 是用到response.set_cookie方法. 一般情况下, 我们不需要直接调用它, 因为:
SessionMiddleware的process_response方法中调用了set_cookie方法.
需要被中间件调用se_cookie方法需要一个条件:
modified = request.session.modified
if modified or settings.SESSION_SAVE_EVERY_REQUEST:

要想request.session.modified == True, 有两种方法:
1. 生成一个新的session对象
2. 修改或者增加session的key对应的值
也就是说, request.session.modified为True. 这涉及到SessionStore类. 下面理理这个类.

SessionStore类

在看django代码时, 会扯到python的一种语法: descriptor. 具体见:
1. http://users.rcn.com/python/download/Descriptor.htm
2. http://www.ibm.com/developerworks/cn/linux/l-python-elegance-2.html

SessionStore类主要起到session管理的作用. 如生成新的session, 修改session中key的value. 检查session_key是否有对应的session对象等等

1. 产生Session对象

SessionStore产生新的session对象(就是新的session_key)有两种方法:
1. load()
2. create()
load是从数据库中对指定session_key对应的session对象, 如果没有就调用create, 所以看create方法:
    def create(self):
        while True:
            self.session_key = self._get_new_session_key() #其实就是产生一个唯一的key
            try:
                # Save immediately to ensure we have a unique entry in the
                # database.
                self.save(must_create=True) #应用了数据库的models对象
            except CreateError:
                # Key wasn't unique. Try again.
                continue
            self.modified = True
            self._session_cache = {}
            return

2. 修改Session实例

在SessionStore类中, 定义了N多对Session实例的操作, 但是这些操作都是引用self._session这个descriptor. 这个descriptor到底返回了什么呢?
   def _get_session(self, no_load=False):

        """
        Lazily loads session from storage (unless "no_load" is True, when only
        an empty dict is stored) and stores it in the current instance.
        """
        self.accessed = True
        try:
            return self._session_cache
        except AttributeError:
            if self._session_key is None or no_load:
                self._session_cache = {}
            else:
                self._session_cache = self.load()
        return self._session_cache

    _session = property(_get_session)

* 引用self._session 其实是引用了self.load(), 这就是descriptor用法!
* 上面的self._session_cache就是session实例的session_data对应的那个dict实例


SessionStore对session实例的一个修改方法:
    def setdefault(self, key, value):
        if key in self._session:
            return self._session[key]
        else:
            self.modified = True
            self._session[key] = value
            return value
* 其中的 self._session[key]其实等于 self.load()[key] = value, 结合self.load的代码后同于 self._session_cache[key] = value

session对应的字典的保存:
在SessionBase中定义了一方法:
    def encode(self, session_dict):
        "Returns the given session dictionary pickled and encoded as a string."
        pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
        pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
        return base64.encodestring(pickled + pickled_md5)
可以看出, 字典是被pickled成一堆字符串了. 需要被使用时, 就使用SessionBase的decode方法


BTW:: 使用eclipse看代码真是他妈的爽!! 前段时间花了不少功夫在eclipse上面, 还是有回报的. 看来除了firefox外, 还有一个一定要是GUI的软件

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.