在 Django 会话中修改字典不会修改会话

Posted

技术标签:

【中文标题】在 Django 会话中修改字典不会修改会话【英文标题】:Modifying Dictionary in Django Session Does Not Modify Session 【发布时间】:2011-01-11 03:09:33 【问题描述】:

我将字典存储在我的会话中,由字符串键引用:

>>> request.session['my_dict'] = 'a': 1, 'b': 2, 'c': 3

我遇到的问题是直接修改字典时,下一次请求时值不会改变:

>>> request.session['my_dict'].pop('c')
3
>>> request.session.has_key('c')
False
# looks okay...
...
# Next request
>>> request.session.has_key('c')
True
# what gives!

【问题讨论】:

我不敢相信在找到这个问题之前我只花了一个半小时来调试这个。感谢您提出这个问题并感谢所有答案。你拯救了我的神经和我剩下的一天。 【参考方案1】:

As thedocumentation states,另一种选择是使用

SESSION_SAVE_EVERY_REQUEST=True

无论如何,这将使每个请求都发生这种情况。如果您的代码中经常发生这种情况,这可能是值得的;我猜偶尔的额外开销不会太多,而且远低于忽略包含的潜在问题

request.session.modified = True

每次排队。

【讨论】:

我刚刚注意到这也意味着每次都会发送一个cookie;对于流量很大的网站,我想这可能会造成带宽问题。另外,我对引用在会话方面的工作方式不是很熟悉,但是如果您执行以下操作会怎样: current_dict=request.session['my_dict'] current_dict.pop('c') request.session['my_dict' ]=current_dict 我认为这会起作用,因为通过设置会话的***键,会话会注意到更改。【参考方案2】:

对于“问”一个我已经知道答案的问题,我深表歉意,但这太令人沮丧了,我认为答案应该记录在 *** 上。如果有人对我的解释有什么要补充的,我将奖励“答案”。我根据问题搜索找不到答案,但是根据答案搜索后我发现我的“问题”是documented behavior。结果也是another person had this problem。

事实证明,SessionBase 是一个类似字典的对象,它会跟踪您何时修改它的键,并手动设置属性modified(还有一个accessed)。但是,如果您在这些键中弄乱对象,则 SessionBase 无法知道对象已被修改,因此您的更改可能不会存储在您使用的任何后端中。 (我正在使用数据库后端;不过,我认为这个问题适用于所有后端。)这个问题可能不适用于模型,因为后端可能正在存储对模型的引用(因此在加载时会收到任何更改数据库中的模型),但问题确实适用于字典(可能还有任何其他必须完全存储在会话存储中的基本 Python 类型。)

诀窍在于,每当您在会话中修改会话不会注意到的对象时,您必须明确告诉会话它已被修改:

>>> request.session.modified = True

希望这对某人有所帮助。

我解决这个问题的方法是将会话上的任何弹出操作封装到一个处理细节的方法中(该方法还接受一个视图参数,以便会话变量可以是特定于视图的):

def session_pop(request, view, key, *args, **kwargs):
    """
    Either returns and removes the value of the key from request.session, or,
    if request.session[key] is a list, returns the result of a pop on this
    list.
    Also, if view is not None, only looks within request.session[view.func_name]
    so that I can store view-specific session variables.
    """
    # figure out which dictionary we want to operate on.
    dicto = 
    if view is None:
        dicto = request.session
    else:
        if request.session.has_key(view.func_name):
            dicto = request.session[view.func_name]

    if dicto.has_key(key):

        # This is redundant if `dicto == request.session`, but rather than
        #  duplicate the logic to test whether we popped a list underneath
        #  the root level of the session, (which is also determined by `view`)
        #  just explicitly set `modified`
        #  since we certainly modified the session here.
        request.session.modified = True

        # Return a non-list
        if not type(dicto[key]) == type(list()):
            return dicto.pop(key)

        # pop a list
        else:
            if len(dicto[key]) > 0:
                return dicto[key].pop()

    # Parse out a default from the args/kwargs
    if len(args) > 0:
        default = args[0]
    elif kwargs.has_key('default'):
        default = kwargs['default']
    else:
        # If there wasn't one, complain
        raise KeyError('Session does not have key "0" and no default was provided'.format(key))
    return default

【讨论】:

回答您的问题无需道歉,这实际上是值得鼓励的!【参考方案3】:

我对此并不感到惊讶。我猜这就像修改元组的内容一样:

a = (1,[2],3)
print a
>>> 1, [2], 3)

a[1] = 4
>>> Traceback (most recent call last):
...  File "<stdin>", line 1, in <module>
...  TypeError: 'tuple' object does not support item assignment

print a
>>> 1, [2], 3)

a[1][0] = 4
print a
>>> 1, [4], 3)

不过还是谢谢。

【讨论】:

a) 您可以修改字典的内容。 b)问题不在于给定变量是否可能;问题是一旦修改它就不会被 Django 自动识别出来

以上是关于在 Django 会话中修改字典不会修改会话的主要内容,如果未能解决你的问题,请参考以下文章

Django中的会话技术(Cookie,Session,Token)

22,23节Django的GET和POST属性笔记

修改会话不保存

怎样在oracle中取出当前序列值

创建临时用户帐户 - Django

为啥不建议在 Alamofire 中修改授权标头的会话配置?