Я прошу прощения за то, что «задал» вопрос, на который я уже знаю ответ, но это было достаточно неприятно, так что я думал, что ответ должен быть записан в 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