【问题标题】:Flask session attribute loses typing after initial loadFlask 会话属性在初始加载后丢失输入
【发布时间】:2015-07-29 02:32:35
【问题描述】:

Flask 的 session 对象似乎正在将 OrderedDict 更改为 dict。这很重要,因为我希望将有关当前用户可访问页面的信息存储在OrderedDict 中,以帮助自动创建导航栏。

我可能会睡眼惺忪,但我不认为我在这里写了任何会覆盖现有数据的东西,所以 AFAICT 它只是无缘无故地转换为 dict

这是在session 对象中设置OrderedDict 的代码。

def safe_route(page_request):
    """Returns the correct page for the user's request.

    If a user requests a non-existent page, 404.
    If user wants a page they shouldn't see, but somehow
    they know about it, redirects to an access-denied page.

    If everything is in order, returns the page the user requests.
    """
    if page_request not in ordered_urls:
        return abort(404)      # early return for 404s

    # get current user and build their navi-dict.
    # this dict will be used to build the navibar in the webpage.
    if not 'registered' in session:
        CurrentUser = get_user(request)
        session['name'] = CurrentUser.name
        session['navi'] = build_user_navi(CurrentUser)
        session['registered'] = True
    if page_request in session['navi']:
        tpl = "/{}.html".format(page_request)
    else:
        # if here, the user requested a page that DOES exist
        # but they do NOT have access to. therefore, they are being
        # goons and require a trip to boxxytown
        tpl = "accessdenied.html"
    return render_template(tpl, on_page=page_request)

它检查以确保会话具有'registered' 标志;如果不是,它会获取用户信息,然后调用build_user_navi,它返回OrderedDict。这是第一次工作;初始页面加载按我最初的意图显示导航栏中的链接。随后单击时,订单丢失了,OrderedDict 上的类型显然也是如此。

render_template 调用之前,我在代码中添加了这一行,看看它是否只是改变了我的类型:

print type(session['navi'])

果然在<class 'collections.OrderedDict'><type 'dict'>之间切换。

有没有一种方法可以将OrderedDict 正确存储为会话数据,或者是否有一些非常好的理由说明这只能在第一次使用时才可以阻止我使用这种方法?

【问题讨论】:

    标签: python session flask


    【解决方案1】:

    会话中的所有数据都需要是 JSON 可序列化的。 OrderedDict 是可序列化的,因为它是 dict 的子类,但是 JSON 中没有有序的 dict,所以在序列化过程中会丢失顺序。

    您可以覆盖how Flask serializes the session 以支持有序字典。这不是最方便的过程,因为您必须复制该类的代码。

    from base64 import b64encode, b64decode
    from collections import OrderedDict
    from datetime import datetime
    import uuid
    from flask import json
    from flask._compat import text_type, iteritems
    from flask.debughelpers import UnexpectedUnicodeError
    from markupsafe import Markup
    from werkzeug.http import http_date, parse_date
    
    
    class TaggedJSONSerializer(object):
        def dumps(self, value):
            def _tag(value):
                if isinstance(value, tuple):
                    return {' t': [_tag(x) for x in value]}
                elif isinstance(value, uuid.UUID):
                    return {' u': value.hex}
                elif isinstance(value, bytes):
                    return {' b': b64encode(value).decode('ascii')}
                elif callable(getattr(value, '__html__', None)):
                    return {' m': text_type(value.__html__())}
                elif isinstance(value, list):
                    return [_tag(x) for x in value]
                elif isinstance(value, datetime):
                    return {' d': http_date(value)}
                elif isinstance(value, OrderedDict):
                    return {'OrderedDict': [[k, _tag(v)] for k, v in iteritems(value)]}
                elif isinstance(value, dict):
                    return dict((k, _tag(v)) for k, v in iteritems(value))
                elif isinstance(value, str):
                    try:
                        return text_type(value)
                    except UnicodeError:
                        raise UnexpectedUnicodeError(u'A byte string with '
                            u'non-ASCII data was passed to the session system '
                            u'which can only store unicode strings.  Consider '
                            u'base64 encoding your string (String was %r)' % value)
                return value
            return json.dumps(_tag(value), separators=(',', ':'))
    
        def loads(self, value):
            def object_hook(obj):
                if len(obj) != 1:
                    return obj
                the_key, the_value = next(iteritems(obj))
                if the_key == ' t':
                    return tuple(the_value)
                elif the_key == ' u':
                    return uuid.UUID(the_value)
                elif the_key == ' b':
                    return b64decode(the_value)
                elif the_key == ' m':
                    return Markup(the_value)
                elif the_key == ' d':
                    return parse_date(the_value)
                elif the_key == 'OrderedDict':
                    return OrderedDict(the_value)
                return obj
            return json.loads(value, object_hook=object_hook)
    

    这里是新标签的演示。 OrderedDict 已正确反序列化。

    s = TaggedJSONSerializer()
    data = OrderedDict((c, i) for i, c in enumerate('abcd'))
    print(data)  # OrderedDict([('a', 0), ('b', 1), ('c', 2), ('d', 3)])
    data = s.dumps(data)
    print(data)  # {"OrderedDict":[["a",0],["b",1],["c",2],["d",3]]}
    data = s.loads(data)
    print(data)  # OrderedDict([('a', 0), ('b', 1), ('c', 2), ('d', 3)])
    

    然后告诉你的应用使用这个序列化器。

    app.session_interface.serializer = TaggedJSONSerializer()
    

    【讨论】:

    • JSON 序列化是这里的关键 - 很好的答案。这是导航栏的很多代码,所以我可能实际上会接受其他发帖人的建议,即使用不同的容器,但我真的喜欢这个
    【解决方案2】:

    我怀疑会话对象在某处被深度复制,OrderedDict 被复制为普通的dict。这可以通过例如找到咨询来源。

    我想出的最简单的解决方法是存储一个简单的键值对列表,而不是 OrderedDict,这很容易迭代并且很容易转换为 dict 的任何国王您是否需要重复快速查找。

    【讨论】:

    • 哦,伙计,我刚刚重构了 dicts 哈哈。这将教会我尝试新事物。
    猜你喜欢
    • 2012-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多