Comportamiento “raro” en Django

En pocas palabras tengo un modelo que obtengo desde su id que me viene por la url. Para esto utilizo la función get_object_or_404 que trae Django. En el medio de la vista de Django aplico algunas funciones a esta instancia del modelo y me estoy encontrando con que luego de aplicar funciones que modifican la base de datos (la fila a la que apunta esta instancia); la instancia no se actualiza y me devuelve valores viejos. ¿Esto está más relacionado con Python que con Django?

El caso que tengo es este (en Pastebin.com):

>>> def modify_model_state(model):
... model.state = 'Closed'
... model.save()
...
>>> model = get_object_or_404(Model, pk=pk_object)
>>> model.state
u'Open'
>>> modify_model_state(model)
>>> model.state
u'Open'
>>> the_same_model = Model.objects.get(id=pk_object)
>>> the_same_model.state
u'Closed'
>>>

En realidad esto está muy simplificado, pero refleja exactamente lo que tengo en mi código y lo que estoy “viviendo”😛

7 pensamientos en “Comportamiento “raro” en Django

  1. humitos dice:

    Mmmm… estoy más desorientado ahora. Probé lo que tengo en la vista en el shell y anda:

    In [1]: battle = Battle.objects.all()[0]

    In [2]: battle
    Out[2]: ... vs ...

    In [3]: def modify_battle(battle):
    ...: battle.state = 'T'
    ...: battle.save()
    ...:
    ...:

    In [4]: battle.state
    Out[4]: u'O'

    In [5]: modify_battle(battle)

    In [6]: battle.state
    Out[6]: 'T'

    In [7]:

  2. humitos dice:

    No tengo configurado ningún middleware para manejar las transacciones de la base de datos y además, leyendo la documentación de Django entiendo que debe funcionar como yo esperaba: http://docs.djangoproject.com/en/1.2/topics/db/transactions/#django-s-default-transaction-behavior

    Django’s default behavior is to run with an open transaction which it commits automatically when any built-in, data-altering model function is called. For example, if you call model.save() or model.delete(), the change will be committed immediately.

  3. javisantana dice:

    El problema es posiblemente que estés haciendo en la vista es tener dos instancias “apuntando” a la misma fila de la BBDD. El comportamiento de django cuando guardas una instancia es almacenar en base de datos los valores de la instancia, pero NO actualiza los valores de las otras instancias que pudiera haber en la aplicación.

    El readme de esta app puede que te solucione el problema (mira el ticket 17, abajo enlazado)
    https://github.com/dcramer/django-idmapper

    Un saludo.

  4. Bahamut dice:

    Igual es una pregunta estúpida pero… ¿es posible que haya alguna otra transacción que este usando también la instancia de ese modelo? Desde alguna otra función o vista no sé… La verdad es que nunca me he cruzado con algo así, es un poco raro.

  5. humitos dice:

    Ayer toote en #django-es me dijo que el problema puede venir de la implementación del model.get y me pasó el link al código:
    * http://code.djangoproject.com/browser/django/tags/releases/1.2.3/django/db/models/query.py

    Todavía no he tenido tiempo de verlo, pero cuando entienda porqué funciona así, lo explicaré…

  6. humitos dice:

    Releyendo el código que estaba ejecutando en la vista, me dí cuenta que no le estoy pasando la instancia del modelo a la función que modifica el estado del mismo, sino que lo está levantando vía pickle y parece que la cosa cambia bastante:
    * http://pastebin.com/PnNfgz5r

    En el ejemplo anterior se ve que en la consola también pasa lo mismo y no como en el caso de mi primer comentario sobre esto.

  7. humitos dice:

    Después de “re-leer” el código de mi vista y publicar ese pastebin Darni vino a explicarnos qué es exactamente lo que está pasando en este caso. Lo que sigue es un fragmento del canal dónde discutimos esto:


    [10:54] humitos: ahhh... es mucho más complicado que lo que escribí en el blog
    [10:54] humitos: weeeeee, la estamos re delirando
    [10:54] humitos: así es la cosa....
    [10:54] humitos: estoy actualizando los campos de un modelo que está "pickleado"
    [10:54] humitos: tomá, chupate esa mandarina
    [10:55] humitos: ahora escribo el caso de ejemplo de nuevo
    [11:00] humitos: Darni: este es el caso "mejor aislado"
    [11:00] humitos: http://pastebin.com/9nNbC6Nq
    [11:03] humitos: aleperalta: ^
    [11:04] aleperalta: humitos: si lo ví, pero me parece que es lo mismo que mostraste antes
    [11:04] humitos: aleperalta: no, ahora estoy usando pickle en vez de pasar la instancia del modelo
    [11:04] humitos: y me parece que por ahí viene el problema
    [11:04] humitos: ok, ahora si te puedo contar que esta pasando ahi
    [11:05] humitos: la instancia que levanto con pickle "no tiene nada que ver" con respecto a la otra
    [11:05] humitos: pero sí apuntan a lo mismo en la base de datos
    [11:05] humitos: ¿es "algo" así?
    [11:05] Darni: en 57 levantas los datos de la db, los guardas en el objeto asociado a "battle"
    [11:06] Darni: en 60 y 62 esencialmente clonas ese objeto. Y llamas a la funcion sobre el clon. Lo que modifica el clon y la base de datos
    [11:06] Darni: pero no el objeto asociado a battle
    [11:06] Darni: y preguntar ".state" no hace un query a la base de datos cada vez (por que seria espantoso a nivel performance)
    [11:07] Darni: no es una cuestion de cache
    [11:07] Darni: tenes nomas muchas copias, y estas actualizando una sola
    [11:08] humitos: Darni: es lo mismo que usar copy.deepcopy
    [11:08] humitos: no?
    [11:08] Darni: es lo mismo que esto:
    [11:08] Darni: a = [1,2,3]
    [11:08] Darni: b = a
    [11:08] Darni: c = a.copy()
    [11:08] Darni: b.append(4)
    [11:09] Darni: entonces a y b van a tener 4 elementos (son la *misma lista) y c va a tener 3
    [11:09] humitos: Darni: bien, entendí
    [11:09] Darni: lo importante es que hacer un get() (o acceder a un elemento de un queryset) es hacer *una copia* de lo que hay en la db
    [11:09] Darni: el save() copia para el otro lado
    [11:09] Darni: y el pickle es hacer otra copia mas :)

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: