Изменение словаря в сеансе Django не изменяет сеанс
Я храню словари в моей сессии, на которые ссылается строковый ключ:
>>> 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
Как указано в документации , другой вариант используйте
SESSION_SAVE_EVERY_REQUEST=True
который сделает это каждый раз в каждом запросе. Возможно, это будет стоить того, если это произойдет в коде; Я предполагаю, что случайные дополнительные накладные расходы не будут много, и это намного меньше, чем потенциальные проблемы от пренебрежения включением
request.session.modified = True
каждый раз.
Ответ 2
Я прошу прощения за "задание" вопроса, на который я уже знаю ответ, но это было настолько расстраивающим, что я думал, что ответ должен быть записан в stackoverflow. Если кто-то может добавить к моему объяснению, я дам "ответ". Я не смог найти ответ, выполнив поиск по этой проблеме, но после поиска, основанного на ответе, я обнаружил, что моя "проблема" документированное поведение. Также получается у другого человека возникла эта проблема.
Оказывается, 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)
Но спасибо в любом случае.