如何在Flask中将会话传递到模板以及应用程序的功能之间? [英] How session is passed to templates and between functions of the app, in Flask?

查看:47
本文介绍了如何在Flask中将会话传递到模板以及应用程序的功能之间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在调查我遇到的另一个难题期间一般而言,在Flask会话中,我试图更好地理解Flask会话的工作原理.


根据有关会话的Flask文档 session对象本身是代理.

我的理解(也就是说,很可能在某种程度上是错误的,这就是这个问题所要涉及的=)意思是:

  1. 代理 session 对象可从应用程序访问,并用于根据需要修改读取的数据

  2. 通常,proxy- 会话会将其更改立即转移到代理- session (会话),因此没有能力/不需要从会话访问 session._get_current_object().模板

  3. 因为字典( session 是字典)是可变的,所以我认为它的id在会话期间应该保持不变(尽管内容可以修改)

  4. 实际的 session (代理对象,可以通过 session._get_current_object()获得)永远不要更改其 ID

现在,当我尝试检查自己的假设时-我遇到的行为使我有些困惑.

考虑以下代码:

my_app.py

从flask导入

 烧瓶,render_template,会议,)app = Flask(__ name__)app.secret_key ='一些随机密钥'@ app.route('/create/')def create():session ['example'] = ['one','two']print_ids()返回str(session ['example'])@ app.route('/modify/')def Modify():session ['example'].append('three')print_ids()返回render_template('my_template.html',id = id)@ app.route('/display/')def display():print_ids()返回str(session ['example'])def print_ids():进口检验calling_function = inspect.stack()[1] [3]打印('')print(calling_function +:会话ID为:{}".format(id(session)))print(calling_function +``:session ['example'] ID为{}''.format(id(session('example'])))打印('________________________________')print(calling_function +:session._get_current_object()ID为:{}".format(id(session._get_current_object()))print(calling_function +:session._get_current_object()['example'] ID为:{}".format(id(session(_session__get_current_object()['example'])))) 

my_template.html

 <!doctype html>< html>< head>< title>显示会话['example']</title></head><身体>< div>{%,如果会话['示例']%}{{session ['example']}}< br/>会话ID为:{{id(session)}}< br/>session ['example'] ID为:{{id(session ['example'])}}< br/>{% 别的 %}没有设置session ['example'] =({% 万一 %}</div></body></html> 

这个想法是打印proxy- session session ['example'] (这是一个列表),代理- session 的ID>(即 session._get_current_object())和代理- session ['example'] (即 session._get_current_object()['example'] >)来自每个函数,以及呈现的模板上的 id(session) id(session ['example']),以跟踪使用了什么在哪里.

以下是结果:

 <代码> .../create/#id(会话)4338311808#id(session._get_current_object())4343709776#id(session ['example'])4343654376#id(session._get_current_object()['example'])4343654376.../调整/#id(会话)4338311808#id(session._get_current_object())4344315984#id(session ['example'])4343652720#id(session._get_current_object()['example'])4343652720呈现了my_template.html#id(会话)4344315984#id(session ['example'])4343652720.../展示/#id(会话)4338311808#id(session._get_current_object())4344471632#id(session ['example'])4341829576#id(session._get_current_object()['example'])4341829576# 再一次.../展示/#id(会话)4338311808#id(session._get_current_object())4344471632#id(session ['example'])4344378072#id(session._get_current_object()['example'])4344378072 

我正在努力理解的是:

  1. 总体上,我对Flask会议概念的误解/错误假设是什么?

  2. 为什么每次显示时更改 session ['example'] session._get_current_object()['example'] 的ID(以及其他所有方法,但是特别显示,因为它不会进行任何修改,所以我希望所有id都保持不变)

  3. 为什么 session._get_current_object()的ID更改而 session 的ID不变?

  4. 由于 session ['example'] session._get_current_object()['example'] 的ID在任何函数的上下文中都是相同的,所以我会假设如果一个对象被更改-那么两个对象都被更改,因为它们是同一个对象.

    话虽如此,并考虑到 session._get_current_object()['example'] 位于代理(即真实") session 内部期望以下内容:

 <代码> .../create/#返回['one','two'].../modify/#将渲染包含['one','two','three']的页面.../display/#return ['one','two','3']作为代理和代理会话,应该已经被修改 

但是作为我之前发现了-这没有发​​生.那么为什么ID相同?

解决方案

您的大部分困惑来自对Flask代理对象的误解,例如 session g 请求.

这些对象所做的所有事情就是确保您获得了当前线程的正确数据;它们在全局对象(可被所有线程访问,易于导入并在Flask代码中使用)和存储在 session.foo 间接访问并返回与 session._get_current_object().foo 完全相同的对象(这就是它们的ID始终匹配的原因).

因此,当访问 session 对象时,代理是透明的.除非您想与另一个线程共享代理对象,否则您不必担心这一切.

为每个请求创建一个新的 您访问的代理对象.这是因为会话的内容取决于每个请求中的数据.Flask的会话机制是可插入的,但是默认实现将所有数据存储在经过加密签名的cookie中,如果您希望能够与之交互,则需要将其解码为Python数据.每个/create//modify//display/ URL均作为单独的请求处理,因此它们均从您的服务器加载会话数据请求进入新的Python对象;他们的ID通常会有所不同.

请求完成后,会话对象再次消失.您别无选择,因为在同一个线程中出现的新请求需要将来自该新请求的会话数据呈现给Flask代码,而不是来自旧请求的数据.

所有这些意味着 id()的输出在此处毫无意义. id()是一个数字,对于当前Python进程中的所有当前 active 对象都是唯一的.这意味着从内存中删除的对象的id可以重复使用,只是因为您在两个时间点看到相同的 id()值并不意味着您具有相同的对象.而且,即使您具有相同的 data (值相等)也不意味着您在内存中具有相同的对象,即使它们的id相同.可以删除旧对象,而只需重新创建一个具有相同值的新对象即可.

在后台,Flask调用 分配给 save_session()方法再次保存会话,并且丢弃该会话对象.默认实现是 SecureSessionInterface 对象在请求中查找特定的cookie,如果存在且具有有效的签名,则将数据解码为带标签的JSON(紧凑型JSON序列化),并返回 session.modified 设置为 True )时发生保存.请注意,默认实现仅在直接操作会话映射(在映射本身中设置,更新或删除键)时才将 modified 设置为 True ,而不是在更改存储在其中的可变对象时会议; session ['foo'] ='bar'是可检测的,但是如果您在会话中存储了列表或字典,则使用诸如 session ['spam'] [0]='ham'将不会被检测到.要么重新设置可变对象( session [key] = session [key] ),要么将 modified 标志手动设置为 True .

During investigation of another conundrum that I had with Flask sessions, I have tried to better my understanding of how Flask sessions work, in general.


According to Flask documentation on sessions session object itself is a proxy.

My understanding (that is, in all probability, wrong in some way, and this is what this question is about =) of what it means is:

  1. the proxy session object is accessed from the app and is used to modify read data as needed

  2. normally, proxy-session will transfer it's changes to proxied-session right away (except for the change of mutables in proxy-session)

  3. in case if proxied-session is busy (in case of a multi-thread app), proxy-session will wait until proxied-session available, and then transfer it's changes to proxied-session

  4. templates receive the 'original' session (i.e. proxied-session), so there is no ability/need to access session._get_current_object() from the templates

  5. as dictionaries (which session is) are mutable, I'd assume it's id should remain unchanged for the length of session (though content could be modified)

  6. the actual session (proxied object, that is available via session._get_current_object()) should never change its ID

Now, when I have tried to check my assumptions - the behaviour I've encountered confused me a bit.

Consider the following code:

my_app.py

from flask import (
Flask,
render_template,
session,
)

app = Flask(__name__)
app.secret_key = 'some random secret key'

@app.route('/create/')
def create():
    session['example'] = ['one', 'two']
    print_ids()
    return str(session['example'])

@app.route('/modify/')
def modify():
    session['example'].append('three')
    print_ids()
    return render_template('my_template.html', id=id)

@app.route('/display/')
def display():
    print_ids()
    return str(session['example'])

def print_ids():
    import inspect
    calling_function = inspect.stack()[1][3]
    print('')
    print(calling_function + ": session ID is: {}".format(id(session)))
    print(calling_function + ": session['example'] ID is {}".format(id(session['example'])))
    print('________________________________')
    print(calling_function + ": session._get_current_object() ID is: {}".format(id(session._get_current_object())))
    print(calling_function + ": session._get_current_object()['example'] ID is: {}".format(id(session._get_current_object()['example'])))

my_template.html

<!doctype html>
<html>
    <head><title>Display session['example']</title></head>
    <body>
        <div>
            {% if session['example'] %}
                {{ session['example'] }}
                <br />
                session ID is: {{ id(session) }}
                <br />
                session['example'] ID is: {{ id(session['example']) }}
                <br />
            {% else %}
                session['example'] is not set =(
            {% endif %}
        </div>
    </body>
</html>

The idea is to print id's of proxy-session, session['example'] (which is a list), proxied-session (i.e. session._get_current_object()) and proxied-session['example'] (i.e. session._get_current_object()['example']) from every function, as well as id(session) and id(session['example']) on the rendered template, in order to track down what is used where.

Here are the results:

.../create/
    # id(session)                                 4338311808 
    # id(session._get_current_object())           4343709776
    # id(session['example'])                                 4343654376
    # id(session._get_current_object()['example'])           4343654376

.../modify/
    # id(session)                                  4338311808
    # id(session._get_current_object())            4344315984
    # id(session['example'])                                  4343652720      
    # id(session._get_current_object()['example'])            4343652720
rendered my_template.html
    # id(session)                                  4344315984
    # id(session['example'])                                  4343652720

.../display/
    # id(session)                                  4338311808         
    # id(session._get_current_object())            4344471632
    # id(session['example'])                                  4341829576
    # id(session._get_current_object()['example'])            4341829576

# one more time
.../display/
    # id(session)                                  4338311808         
    # id(session._get_current_object())            4344471632
    # id(session['example'])                                  4344378072
    # id(session._get_current_object()['example'])            4344378072

Things I'm striving to understand are:

  1. What are my misunderstandings/wrong assumptions, concerning Flask sessions concept, in general?

  2. Why ids of session['example'] and session._get_current_object()['example'] are changed on every hit of display (and every other method, but display in particular, since it does not modify anything, I would expect all ids to remain unchanged)?

  3. Why id of session._get_current_object() changes and id of session is not?

  4. Since ids of session['example'] and session._get_current_object()['example'] are identical in context of any function, I would assume that if one object is changed - then both are changed, as they are the same object.

    With that being said, and taking into account that session._get_current_object()['example'] is inside proxied (i.e. 'real') session I'd expect the following:

    .../create/ # return ['one', 'two']
    .../modify/ # will render page containing ['one', 'two', 'three']
    .../display/ # return ['one', 'two', 'three'] as proxy and proxied sessions should have been modified

But as I have previously discovered - it's not happening. So why ids are the same?

解决方案

Most of your confusion comes from misunderstandings about Flask proxy objects, such as session, g and request.

All that these objects do is make sure you get the correct data for the current thread; they proxy between a global object (accessible by all threads, easy to import and use in your Flask code), to an object stored in thread-local storage, which is an object that transparently differentiates attribute access by thread id. There is no need for locking or 'waiting' in this, proxied objects are never used by more than one thread. session.foo indirectly accesses and returns the exact same object as session._get_current_object().foo does (which is why their ids always match).

So when accessing the session object, proxying is transparent. This is not something you ever need worry about unless you want to share a proxied object with another thread.

The proxied object you access is created a-new for every request. That's because the contents of a session are dependent on data in each request. Flask's session machinery is pluggable, but the default implementation stores all data in a cryptographically signed cookie, which needs to be decoded into Python data if you want to be able to interact with it. Each of your /create/, /modify/ and /display/ URLs are handled as separate requests, so they all load session data from your request into new Python objects; their ids will usually differ.

After a request is done, the session object is gone again. You can't have this any other way, because a new request coming in on the same thread needs to present the session data from that new request to your Flask code, not the data from the old request.

All this means that the output of id() is meaningless here. id() is a number that is unique for all currently active objects in the current Python process. This means that the ids of objects that are removed from memory can be reused, just because you have seen the same id() value at two points in time doesn't mean you have the same object. And just because you have the same data (value equality) doesn't mean that you have the same object in memory, even if their id is the same. The old object could have been deleted and a new object could simply have been re-created with the same value.

Under the hood, Flask calls the open_session() method on the object assigned to Flask().session_interface at the start of each request. At the end, the save_session() method is called to save the session again, and the session object is discarded. The default implementation is the SecureSessionInterface object, which looks for a specific cookie on the request, and if present and with a valid signature, decodes the data as tagged JSON (a compact JSON serialisation), and returns a SecureCookieSession instance with that data. This is the object that session proxies for, and is returned by session._get_current_object(). When saving, the data is serialised to tagged JSON again, signed, and added to the response as an outgoing Set-Cookie header.

Saving only happens when the session object has been 'changed', (session.modified is set to True). Note that the default implementation only sets modified to True when directly manipulating the session mapping (setting, updating or deleting keys in the mapping itself), not when changing mutable objects stored in the session; session['foo'] = 'bar' is detectable, but if you stored a list or dictionary in the session, then mutating those with expressions like session['spam'][0] = 'ham' will not be detected. Either re-set the mutable object (session[key] = session[key]) or set the modified flag to True manually.

这篇关于如何在Flask中将会话传递到模板以及应用程序的功能之间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆