补充一下
request是在哪里产生的:
class RequestContext(object):
# app就是flask对象
self.app = app
if request is None:
request = app.request_class(environ) # Request(environ) 处理request
self.request = request # 就是这里没错了
self.url_adapter = app.create_url_adapter(self.request)
self.flashes = None
self.session = None
请求流程
session的存取过程
session的取值过程位于上图的此部分
Flask实例的open_session方法首先是取了session_interface的值--一个类(SecureCookieSessionInterface)的对象,调用了他的open_session方法,将取出来的值赋予了ctx.session,存储到了Local实例中
session的存值过程
则是调用了session_interface代表的类实例的save_session方法但是这里存在一个问题:
class SecureCookieSessionInterface(SessionInterface):
session_class = SecureCookieSession
def open_session(self, app, request):
return self.session_class() # ctx的session就是这个类的实例
# 父类中的此方法,我给直接写到这里了,如果此函数返回False,save_session不会设置cookie
def should_set_cookie(self, app, session):
if session.modified:
# 如果session.modified为True就说明session修改过了,为什么说修改过了,看下面
return True
save_each = app.config['SESSION_REFRESH_EACH_REQUEST']
# session.permanent在SessionMixin类中
return save_each and session.permanent def save_session(self, app, session, response):
# 保存session时会根据session的modified来判断将不将session写进cookie中,下面简写
if not self.should_set_cookie(app, session):
return # session的类
class SecureCookieSession(CallbackDict, SessionMixin):
def __init__(self, initial=None):
def on_update(self):
self.modified = True
CallbackDict.__init__(self, initial, on_update)
self.modified = False # 默认是False class CallbackDict(UpdateDictMixin, dict):
def __init__(self, initial=None, on_update=None):
dict.__init__(self, initial or ())
self.on_update = on_update # 这个就是上面init定义的方法 class UpdateDictMixin(object):
# 这个类中定义了__setitem__和__delitem__ 会去执行self.on_update,修改self.modified的值
# 问题就出在这里,session["x"]=""才会去执行self.on_update,
# 但是如果session["x"]对应的是个数据结构,修改这个数据结构中的元素并不会去执行elf.on_update,
# session.modified就还是False,修不修改session就要看save_each and session.permanent了
# 如果他们之间有一个false就不会去修改,可以直接看到save_each存在与配置中
# ,而session.permanent在SessionMixin类中
on_update = None
def calls_update(name):
def oncall(self, *args, **kw):
rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw)
if self.on_update is not None:
self.on_update(self)
return rv
oncall.__name__ = name
return oncall def setdefault(self, key, default=None):
modified = key not in self
rv = super(UpdateDictMixin, self).setdefault(key, default)
if modified and self.on_update is not None:
self.on_update(self)
return rv def pop(self, key, default=_missing):
modified = key in self
if default is _missing:
rv = super(UpdateDictMixin, self).pop(key)
else:
rv = super(UpdateDictMixin, self).pop(key, default)
if modified and self.on_update is not None:
self.on_update(self)
return rv __setitem__ = calls_update('__setitem__')
__delitem__ = calls_update('__delitem__')
clear = calls_update('clear')
popitem = calls_update('popitem')
update = calls_update('update')
del calls_update # session.permanent相关的
class SessionMixin(object):
# 取值时走这个方法
def _get_permanent(self):
return self.get('_permanent', False)
# 通过.设置值是走这个方法
def _set_permanent(self, value):
self['_permanent'] = bool(value)
# 这里不明白,为他设置值的时候会去调用on_update修改modified = True,如果设置了他
# SessionInterface中的should_set_cookie就会直接return True
# 而如果没设置就直接return session.permanent就好了,为什么还要and save_each
permanent = property(_get_permanent, _set_permanent)
del _get_permanent, _set_permanent new = False modified = True
将session存在其他地方
要将session存在其他地方,主要是和Flask中的session_interface属性对应的类实例有关系的.这些类要提供save_session和open_session方法
Flask的flask_session组件就提供了这些类
安装
pip install flask_session
用法
# 配置类,具体的配置是根据数据库类型设置的
class Config(object):
SECRET_KEY = "umsuldfsdflskjdf" # 加密用
PERMANENT_SESSION_LIFETIME = timedelta(minutes=20)
SESSION_TYPE = "redis"
SESSION_REDIS = Redis(host='192.168.0.94', port='6379') # 数据库连接,可以用连接池 from flask import Flask,session
from flask_session import Session
app = Flask(__name__)
app.config.from_object('Config')
Session(app)
Session干了什么
class Session(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app) def init_app(self, app):
# 为flase实例赋了新的实例
app.session_interface = self._get_interface(app) def _get_interface(self, app):
# 根据配置来匹配不同的类
config = app.config.copy()
config.setdefault('SESSION_TYPE', 'null')
config.setdefault('SESSION_PERMANENT', True)
config.setdefault('SESSION_USE_SIGNER', False)
config.setdefault('SESSION_KEY_PREFIX', 'session:')
config.setdefault('SESSION_REDIS', None)
config.setdefault('SESSION_MEMCACHED', None)
config.setdefault('SESSION_FILE_DIR',
os.path.join(os.getcwd(), 'flask_session'))
config.setdefault('SESSION_FILE_THRESHOLD', 500)
config.setdefault('SESSION_FILE_MODE', 384)
config.setdefault('SESSION_MONGODB', None)
config.setdefault('SESSION_MONGODB_DB', 'flask_session')
config.setdefault('SESSION_MONGODB_COLLECT', 'sessions')
config.setdefault('SESSION_SQLALCHEMY', None)
config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions') if config['SESSION_TYPE'] == 'redis':
session_interface = RedisSessionInterface(
config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'memcached':
session_interface = MemcachedSessionInterface(
config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'filesystem':
session_interface = FileSystemSessionInterface(
config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'],
config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'mongodb':
session_interface = MongoDBSessionInterface(
config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'],
config['SESSION_MONGODB_COLLECT'],
config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'sqlalchemy':
session_interface = SqlAlchemySessionInterface(
app, config['SESSION_SQLALCHEMY'],
config['SESSION_SQLALCHEMY_TABLE'],
config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
config['SESSION_PERMANENT'])
else:
session_interface = NullSessionInterface() return session_interface