45 votes

La modification du dictionnaire dans la session Django ne modifie pas la session

Je stocke des dictionnaires dans ma session référencés par une clé de chaîne:

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

Le problème que j'ai rencontré était que lorsque je modifiais directement le dictionnaire, la valeur ne serait pas modifiée lors de la prochaine requête:

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

41voto

Jordan Reiter Points 8679

Comme l'indique la documentation, une autre option consiste à utiliser

 SESSION_SAVE_EVERY_REQUEST=True
 

ce qui rendra cela possible à chaque demande de toute façon. Cela pourrait valoir la peine si cela se produit souvent dans votre code; Je suppose que les frais généraux supplémentaires occasionnels ne seraient pas beaucoup et ils sont beaucoup moins importants que les problèmes potentiels liés à la négligence d'inclure le

 request.session.modified = True
 

ligne à chaque fois.

17voto

Carl G Points 2025

Je m'excuse pour "poser" une question dont je connais déjà la réponse, mais c'était assez frustrant que je pensais que la réponse doit être enregistrée sur stackoverflow. Si quelqu'un a quelque chose à ajouter à mon explication je vais le prix de la "réponse". Je ne pouvais pas trouver la réponse par la recherche basée sur le problème, mais après une recherche basée sur la réponse, j'ai trouvé que mon "problème" est le comportement documenté. S'avère également une autre personne a eu ce problème.

Il s'avère que SessionBase est un objet de type dictionnaire qui garde une trace de quand de le modifier avec les touches, et définit manuellement un attribut modified (il y a aussi un accessed). Si vous vous trompez dans les objets à l'intérieur de ces clés, cependant, SessionBase n'a aucun moyen de savoir que les objets sont modifiés, et par conséquent, vos changements ne sont pas stockés dans quel backend vous utilisez. (Je suis en utilisant une base de données backend; je suppose que ce problème s'applique à tous les backends, cependant). Ce problème peut ne pas s'appliquer à des modèles, depuis le backend est probablement stocker une référence du modèle (et donc recevoir tout change quand il chargé le modèle de la base de données), mais le problème ne s'appliquent à des dictionnaires (et peut-être toute autre base de python les types qui doivent être entièrement stockées dans la session magasin.)

Le truc, c'est qu'à chaque fois que vous modifiez des objets à la session et la session ne sera pas un avis, vous devez indiquer explicitement à la session qu'il soit modifié:

>>> request.session.modified = True

Espérons que cela aide quelqu'un.

La façon dont je suis arrivé c'était pour encapsuler toute pop-actions sur la séance, dans une méthode qui prend soin des détails (cette méthode accepte un paramètre affichage de sorte que les variables de session peut être spécifique à la vue):

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

4voto

George Stocker Points 31258

C'est un CW réponse de l'OP réponse originale à cette question (qui a été collé dans la question):

Je m'excuse pour "poser" une question dont je connais déjà la réponse, mais c'était assez frustrant que je pensais que la réponse doit être enregistrée sur stackoverflow. Si quelqu'un a quelque chose à ajouter à mon explication je vais le prix de la "réponse". Je ne pouvais pas trouver la réponse par la recherche basée sur le problème, mais après une recherche basée sur la réponse, j'ai trouvé que mon "problème" est le comportement documenté. S'avère également une autre personne a eu ce problème.

Il s'avère que SessionBase est un objet de type dictionnaire qui garde une trace de quand de le modifier avec les touches, et définit manuellement un attribut modified (il y a aussi un accessed). Si vous vous trompez dans les objets à l'intérieur de ces clés, cependant, SessionBase n'a aucun moyen de savoir que les objets sont modifiés, et par conséquent, vos changements ne sont pas stockés dans quel backend vous utilisez. (Je suis en utilisant une base de données backend; je suppose que ce problème s'applique à tous les backends, cependant). Ce problème peut ne pas s'appliquer à des modèles, depuis le backend est probablement stocker une référence du modèle (et donc recevoir tout change quand il chargé le modèle de la base de données), mais le problème ne s'appliquent à des dictionnaires (et peut-être toute autre base de python les types qui doivent être entièrement stockées dans la session magasin.)

Le truc, c'est qu'à chaque fois que vous modifiez des objets à la session et la session ne sera pas un avis, vous devez indiquer explicitement à la session qu'il soit modifié:

>>> request.session.modified = True

Espérons que cela aide quelqu'un.

La façon dont je suis arrivé c'était pour encapsuler toute pop-actions sur la séance, dans une méthode qui prend soin des détails (cette méthode accepte un paramètre affichage de sorte que les variables de session peut être spécifique à la vue):

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

-4voto

wisty Points 4280

Je ne suis pas trop surpris par ça. Je suppose que c'est comme modifier le contenu d'un tuple:

 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)
 

Mais merci quand même.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X