SessionBase之所以可当作字典来操作,因为它本身就是对字典的包装。所以需要了解一下python的一些魔法方法。
举个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
DictWrapper:
def
__init__(
self
):
self
.
dict
=
{}
def
__getitem__(
self
, key):
return
self
.
dict
[key]
def
__setitem__(
self
, key, value):
self
.
dict
[key]
=
value
def
__delitem__(
self
, key):
del
self
.
dict
[key]
def
__contains__(
self
, key):
return
key
in
self
._session
|
__getitem__ 负责获取数据。
__setitem__负责改变数据。
__delitem__负责删除数据。
__contains__负责判断是否包含这个key。
然后看SessionBase的定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
class
SessionBase(
object
):
"""
Base class for all Session classes.
"""
TEST_COOKIE_NAME
=
'testcookie'
TEST_COOKIE_VALUE
=
'worked'
def
__init__(
self
, session_key
=
None
):
self
._session_key
=
session_key
self
.accessed
=
False
self
.modified
=
False
self
.serializer
=
import_string(settings.SESSION_SERIALIZER)
def
__contains__(
self
, key):
return
key
in
self
._session
def
__getitem__(
self
, key):
return
self
._session[key]
def
__setitem__(
self
, key, value):
self
._session[key]
=
value
self
.modified
=
True
def
__delitem__(
self
, key):
del
self
._session[key]
self
.modified
=
True
def
get(
self
, key, default
=
None
):
return
self
._session.get(key, default)
def
pop(
self
, key,
*
args):
self
.modified
=
self
.modified
or
key
in
self
._session
return
self
._session.pop(key,
*
args)
def
update(
self
, dict_):
self
._session.update(dict_)
self
.modified
=
True
def
has_key(
self
, key):
return
key
in
self
._session
def
keys(
self
):
return
self
._session.keys()
def
values(
self
):
return
self
._session.values()
def
items(
self
):
return
self
._session.items()
def
iterkeys(
self
):
return
self
._session.iterkeys()
def
itervalues(
self
):
return
self
._session.itervalues()
def
iteritems(
self
):
return
self
._session.iteritems()
|
除了上述几个魔法方法, SessionBase还实现了dict的其他的方法。
基本上都是_session的包装。
现在来看看_session的定义:
1
|
_session
=
property
(_get_session)
|
_session的值是_get_session方法返回的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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
|
_get_session方法会优先去获取_session_cache这个缓存变量的值,如果没有则调用load方法。
注意self.accessed属性的改变。它会记录是否获取过session。
1
2
3
4
5
|
def
load(
self
):
"""
Loads the session data and returns a dictionary.
"""
raise
NotImplementedError(
'subclasses of SessionBase must provide a load() method'
)
|
load方法需要子类定义,返回session-data。
一般load实现要注意这种情况,用户第一次登陆是没有_session_cache。注意_get_session方法,它会通过捕获AttributeError异常,判断是否存在_session_cache。如果没有会调用load方法。
所以当用户没有_session_cache的时候,需要返回一个空字典。
注意一下clear的实现。因为执行clear函数时,如果self._session有可能会调用load函数,导致不必要的性能开销。
所以直接复制_session_cache为空。
1
2
3
4
5
6
7
|
def
clear(
self
):
# To avoid unnecessary persistent storage accesses, we set up the
# internals directly (loading data wastes time, since we are going to
# set it to an empty dict anyway).
self
._session_cache
=
{}
self
.accessed
=
True
self
.modified
=
True
|
因为session和cookie会有一个有效期,以'_session_expiry'存在_session_data里。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
def
get_expiry_date(
self
,
*
*
kwargs):
"""Get session the expiry date (as a datetime object).
Optionally, this function accepts `modification` and `expiry` keyword
arguments specifying the modification and expiry of the session.
"""
try
:
modification
=
kwargs[
'modification'
]
except
KeyError:
modification
=
timezone.now()
# Same comment as in get_expiry_age
try
:
expiry
=
kwargs[
'expiry'
]
except
KeyError:
expiry
=
self
.get(
'_session_expiry'
)
if
isinstance
(expiry, datetime):
return
expiry
if
not
expiry:
# Checks both None and 0 cases
expiry
=
settings.SESSION_COOKIE_AGE
return
modification
+
timedelta(seconds
=
expiry)
|
这里注意_session_expiry对应的值,可能是为int,也可能是datetime类型。如果为int,则表示剩下的有效期,以second为单位。如果为datetime类型,则为有效日期,需要计算两者的时间差,也是以second为单位。
get_expire_at_browser_close方法用来判断session的截至时间是否为关闭浏览器的时间。
当_session_expiry为None并且settings.SESSION_EXPIRE_AT_BROWSER_CLOSE设置为true,
当_session_expiry为0,
都会返回true。
1
2
3
4
5
6
7
8
9
10
|
def
get_expire_at_browser_close(
self
):
"""
Returns ``True`` if the session is set to expire when the browser
closes, and ``False`` if there's an expiry date. Use
``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry
date/age, if there is one.
"""
if
self
.get(
'_session_expiry'
)
is
None
:
return
settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
return
self
.get(
'_session_expiry'
)
=
=
0
|