2010-01-30 13 views
37

estoy almacenando diccionarios en mi sesión de referencia una clave de cadena:Diccionario de modificación dentro de Django Sesión no modifica Sesión

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

El problema que encontramos fue que cuando he modificado el diccionario directamente, el valor no sería cambiado durante la próxima solicitud:

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

No puedo creer que acabo de pasar una hora y media arrancando la depuración antes de encontrar esta pregunta. Gracias por hacer esta pregunta y gracias a todas las respuestas. Me salvaste los nervios y el resto de mi día. –

Respuesta

32

As thedocumentation states, otra opción es utilizar

SESSION_SAVE_EVERY_REQUEST=True 

lo que hará que esto suceda cada petición de todos modos. Puede valer la pena si esto sucede mucho en su código; Supongo que la sobrecarga adicional ocasional no sería mucho más y es mucho menos que los problemas potenciales de descuidar de la inclusión de la línea

request.session.modified = True 

cada vez.

+0

Me di cuenta de que esto también significa que se enviará una cookie cada vez; para sitios con mucho tráfico, supongo que esto podría suponer un problema de ancho de banda. Además, no estoy muy familiarizado con la forma en referencias de trabajo en términos de sesiones, pero lo que si se hizo algo como: current_dict = request.session [ 'my_dict'] current_dict.pop ('c') solicitud .session ['my_dict'] = current_dict –

+0

Creo que funcionaría, ya que al establecer la clave de nivel superior de la sesión, la sesión notaría el cambio. –

-3

No me sorprende demasiado. Supongo que es como modificar el contenido de una tupla:

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) 

Pero gracias de todos modos.

+1

a) Puede modificar el contenido de un diccionario.b) el problema no es si es posible o no para una variable dada; el problema es que una vez modificado, Django no lo reconoce automáticamente. –

8

Me disculpo por "hacer" una pregunta a la que ya sé la respuesta, pero esto fue lo suficientemente frustrante que pensé que la respuesta debería registrarse en stackoverflow. Si alguien tiene algo que agregar a mi explicación, otorgaré la "respuesta". No pude encontrar la respuesta buscando basándose en el problema, pero después de buscar según la respuesta encontré que mi "problema" es documented behavior. También resulta another person had this problem.

Resulta que SessionBase es un objeto similar al diccionario que realiza un seguimiento de cuándo se modifican sus claves, y establece manualmente un atributo modified (también hay un accessed). Si te metes con los objetos dentro de las esas teclas, sin embargo, SessionBase no tiene forma de saber que los objetos están modificados, y por lo tanto tus cambios podrían no almacenarse en el back-end que estés utilizando. (Estoy usando un back-end de base de datos; supongo que este problema se aplica a todos los back-ends). Este problema podría no aplicarse a los modelos, ya que el backend probablemente almacena una referencia al modelo (y por lo tanto recibiría cambios cuando se cargara). el modelo de la base de datos), pero el problema se aplica a los diccionarios (y quizás a cualquier otro tipo base de python que deba almacenarse por completo en el almacén de sesiones).

El truco es que cada vez que modifique objetos en la sesión el sesión no se dará cuenta, deberá informar a la sesión explícitamente que se modifica:

>>> request.session.modified = True 

Espero que esto ayude a alguien.

La forma llegué alrededor de esta era para encapsular cualquier acción pop en la sesión en un método que toma el cuidado de los detalles (este método también acepta un parámetro de vista para que las variables de sesión pueden ser específica de la vista):

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

¡No hay necesidad de disculparse por responder sus preguntas, de hecho se lo alienta! – Flimm

Cuestiones relacionadas