如何重新排列函数以将with语句与Flask的装饰器结合起来?

时间:2017-09-28 16:52:00

标签: python session flask

我已经构建了一个自定义会话处理程序,它将重要位存储在数据库中,只是将ID保存在默认的Flask会话中(这是一个加密的cookie)。每次加载页面时,会读取会话数据(或使用新ID创建),然后在退出时会话将保存到数据库中。我基本上遵循PHP会话的想法,但将其保存在数据库中而不是文件中。

我已使用__enter____exit__进行设置,以便像这样工作:

@app.route('/')
def main():
    with SessionHandler(mysql) as session:

        #Get a new session ID but keep the session data
        session.regenerate()

        #Add 1 to session variable
        try:
            session['count'] += 1
        except KeyError:
            session['count'] = 1

        #Other code here

我正在考虑手动执行登录(例如,如果用户未登录,则重定向到页面),但我遇到了@login_required,这似乎是一种不错的做事方式。

这是其中一个默认实现:

def login_required(function_to_protect):
    @wraps(function_to_protect)
    def wrapper(*args, **kwargs):
        user_id = session.get('user_id')
        if user_id:
            user = database.get(user_id)
            if user:
                # Success!
                return function_to_protect(*args, **kwargs)
            else:
                flash("Session exists, but user does not exist (anymore)")
                return redirect(url_for('login'))
        else:
            flash("Please log in")
            return redirect(url_for('login'))

@app.route('/')
@login_required
def main():
    return 'you are logged in'

但是,如果我要将其与我自己的会话变量相关联,那么我必须在每个页面的会话上进行另一次数据库查找。

有没有办法可以将这两种方法结合起来,这样我就可以在装饰器中访问会话数据而无需再次从数据库中读取数据?如果我可以简化with SessionManager(mysql) as session部分,它也是一个奖励,因为每个功能现在都有2个级别的缩进,我不太确定是否有方法绕过

如果它有用,可以在这里上课(仍为在制品):

class SessionManager(object):
    def __init__(self, db_connection):
        self.sql = db_connection.sql

    def __enter__(self):
        try:
            session_id = session['sid']
            hash = quick_hash(session_id)
            session_data = self.sql('SELECT data_pickle, last_activity FROM temporary_storage WHERE id = %s', hash)
            if session_data and session_data[0][1] > time.time() - SESSION_TIMEOUT:
                self.hash = hash
                self.data = cPickle.loads(session_data[0][0])
                self._new_id = False
                return self
            else:
                raise KeyError
        except KeyError:
            self.new()
            return self

    def __getitem__(self, item):
        return self.data[item]

    def __setitem__(self, item, value):
        self.data[item] = value

    def get(self, item, default):
        return self.data.get(item, default)

    def new(self):
        self.regenerate()
        self.data = {}

    def regenerate(self):
        while True:
            session_id = uuid.uuid4().hex
            hash = quick_hash(session_id)
            if not self.sql('SELECT count(*) FROM temporary_storage WHERE id = %s', hash):
                session['sid'] = session_id
                self.hash = hash
                self._new_id = True
                return session_id

    def __exit__(self, *args):
        data = cPickle.dumps(self.data)
        if self._new_id:
            self.sql('INSERT INTO temporary_storage (id, data_pickle, last_activity) VALUES(%s, %s, UNIX_TIMESTAMP(NOW()))', self.hash, data)
        else:
            self.sql('UPDATE temporary_storage SET data_pickle = %s, last_activity = UNIX_TIMESTAMP(NOW()) WHERE id = %s', data, self.hash)

1 个答案:

答案 0 :(得分:1)

你可以创建一个带有这样一个参数的装饰器:

awk '{print FILENAME, $0}' "${PFILE}"

最外层函数将装饰器创建为一个知道def with_session_handler(driver): def decorator(func_to_wrap): @wraps(func_to_wrap) def wrapper(*args, **kwargs): with SessionHandler(driver) as session: return func_to_wrap(*args, **kwargs, session=session) return wrapper return decorator 对象的闭包。它一直显示在driver函数中,您可以在调用包装函数时在wrapper语句中使用它。

然后使用它:

with