Изменение словаря в сеансе Django не изменяет сеанс - PullRequest
38 голосов
/ 30 января 2010

В моем сеансе хранятся словари, на которые ссылается строковый ключ:

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

36 голосов
/ 30 января 2010

Поскольку в документации указано , можно использовать

.
SESSION_SAVE_EVERY_REQUEST=True

, что все равно сделает это при каждом запросе. Может быть, это того стоит, если это часто происходит в вашем коде; Я предполагаю, что случайные дополнительные издержки не будут значительными, и это намного меньше, чем потенциальные проблемы от пренебрежения включением

request.session.modified = True

строка каждый раз.

10 голосов
/ 28 февраля 2014

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

Я не слишком удивлен этим. Я полагаю, это похоже на изменение содержимого кортежа:

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)

Но все равно спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...