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

时间:2010-01-30 04:27:05

标签: django session

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

>>> 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!

3 个答案:

答案 0 :(得分:35)

As the documentation states,另一种选择是使用

SESSION_SAVE_EVERY_REQUEST=True

无论如何都会使这一切发生。如果你的代码中发生了很多事情,那么可能是值得的;我猜测偶尔额外的开销不会太大,而且远远少于忽视包括

的潜在问题
request.session.modified = True
每次都行。

答案 1 :(得分:10)

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

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

诀窍在于,无论何时修改会话中没有注意到的会话中的对象,都必须明确告诉会话它已被修改:

>>> request.session.modified = True

希望这有助于某人。

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

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

答案 2 :(得分:-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)

但无论如何,谢谢。