【问题标题】:How session is passed to templates and between functions of the app, in Flask?在 Flask 中,会话如何传递给模板以及应用程序的功能之间?
【发布时间】:2019-04-24 11:06:53
【问题描述】:

在我对 Flask 会话的 another conundrum 进行调查期间,我试图更好地理解 Flask 会话的一般工作原理。


根据Flask documentation on sessions session 对象本身就是一个proxy

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

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

  2. 通常,proxy-session 会立即将其更改转移到 proxied-session(除了 proxy-session 中的 change of mutables

  3. 如果 proxied-session 忙(如果是多线程应用程序),proxy-session 将等到 proxied-session 可用,然后将其更改传输到 proxied-session

  4. 模板接收“原始”会话(即代理-session),因此没有能力/不需要从模板访问session._get_current_object()

  5. 由于字典(session 是)是可变的,我假设它的 id 在会话期间应该保持不变(尽管可以修改内容)

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

现在,当我尝试检查我的假设时 - 我遇到的行为让我有点困惑。

考虑以下代码:

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>

这个想法是从每个函数,以及渲染模板上的id(session)id(session['example']),以便追踪在哪里使用了什么。

结果如下:

.../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

我正在努力理解的事情是:

  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/ # 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

但是作为I have previously discovered - 它没有发生。那么为什么 id 是一样的呢?

【问题讨论】:

    标签: python session flask proxy


    【解决方案1】:

    您的大部分困惑来自对 Flask 代理对象的误解,例如 sessiongrequest

    这些对象所做的只是确保您获得当前线程的正确数据;它们在全局对象(所有线程都可以访问,易于在 Flask 代码中导入和使用)与存储在 thread-local storage 中的对象之间代理,该对象是通过线程 ID 透明区分属性访问的对象。在这种情况下不需要锁定或“等待”,代理对象永远不会被多个线程使用。 session.foo 间接访问并返回与 session._get_current_object().foo 完全相同的对象(这就是它们的 id 始终匹配的原因)。

    所以当访问session 对象时,代理是透明的。除非您想与另一个线程共享代理对象,否则您无需担心这一点。

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

    请求完成后,会话对象又消失了。您不能以任何其他方式使用此方法,因为来自同一线程的新请求需要将来自该新请求的会话数据呈现给您的 Flask 代码,而不是来自旧请求的数据。

    这一切意味着id() 的输出在这里是没有意义的id() 是当前 Python 进程中所有当前活动对象的唯一编号。这意味着从内存中删除的对象的 id 可以重复使用,仅仅因为您在两个时间点看到相同的 id() 值并不意味着您拥有相同的对象。仅仅因为你有相同的 data (值相等)并不意味着你在内存中有相同的对象,即使它们的 id 是相同的。旧对象可能已被删除,而新对象可以简单地使用相同的值重新创建。

    在后台,Flask 在每个请求开始时调用分配给Flask().session_interface 的对象上的open_session() method。最后再次调用save_session() method保存会话,丢弃会话对象。默认实现是SecureSessionInterface object,它在请求中查找特定的cookie,如果存在并且具有有效签名,则将数据解码为标记的JSON(紧凑的JSON序列化),并返回带有该数据的SecureCookieSession instance .这是session 代理的对象,由session._get_current_object() 返回。保存时,数据会再次序列化为带标签的 JSON、签名并作为传出的 Set-Cookie 标头添加到响应中。

    仅当会话对象已“更改”时才会进行保存(session.modified 设置为True)。请注意,默认实现仅在直接操作会话映射(设置、更新或删除映射本身的键)时将modified 设置为True,而不是在更改存储在会话中的可变对象时; session['foo'] = 'bar' 是可检测到的,但如果您在会话中存储了列表或字典,则不会检测到使用 session['spam'][0] = 'ham' 之类的表达式进行变异。重新设置可变对象 (session[key] = session[key]) 或手动将 modified 标志设置为 True

    【讨论】:

    • 非常感谢,这么详细的回答!我特别感谢在问题的上下文中详细解释了 id() 值的无意义。
    【解决方案2】:

    奉献=):这个答案只是感谢用户:brunnsshmee 以及他们对我的其他问题的回答:1(brunns)2(shmee)


    以下是 (my own) 问题列表的答案:

    1. 主要错误是:是的——session是一个代理,是的——session代理的对象由session._get_current_object()返回,但是: 代理的对象session 的每个请求都不同

    2. 这是因为 session 代理的对象(以及它包含的所有内容)对于每个请求都是不同的。有关详细信息:请参阅下面第 3 点的答案。

    3. 嗯:

      • this answer 向我指出,session 是从 flask 模块导入的对象,并且只导入一次 - 它的 id() 永远不会改变

      • 每个请求都有一个基础对象(由session._get_current_object() 返回),正如an answer 对另一个问题的建议,以及Flask documentation — 由session 代理的对象属于RequestContext因此对于每个新请求都是不同的。因此,不同请求的值不同(这里唯一的模糊之处是 sometimes session._get_current_object() 在连续请求之间保持不变,正如在同一 answer 中指出的那样(粗体是我的),它是:

        可能,因为新会话对象创建在与前一个请求中的旧会话对象所占用的内存地址相同的内存地址中。

    4. 这里的期望是错误的——而不是结果。 session['example'] 没有从一个请求修改到另一个请求的原因在documentation on modified attribute of a session 中有明确说明:

      请注意,不接受对可变结构的修改 自动,在这种情况下,您必须明确设置 归功于True你自己。

      由于session['example']list 而列表是mutable — 为了让更改被拾取,我们需要更改modify 函数的代码,如下所示:

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

      更改后:

      .../create/ # returns ['one', 'two']
      .../modify/ # renders page containing ['one', 'two', 'three']
      .../display/ # returns ['one', 'two', 'three']
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-01
      • 2012-06-02
      • 2018-06-18
      • 2010-09-09
      • 2011-12-21
      • 1970-01-01
      • 2021-08-30
      • 1970-01-01
      相关资源
      最近更新 更多