Recargar objeto django desde la base de datos

161

¿Es posible actualizar el estado de un objeto django desde la base de datos? Me refiero a un comportamiento más o menos equivalente a:

new_self = self.__class__.objects.get(pk=self.pk)
for each field of the record:
    setattr(self, field, getattr(new_self, field))

ACTUALIZACIÓN: Encontró una guerra de reapertura / corrección en el rastreador: http://code.djangoproject.com/ticket/901 . Todavía no entiendo por qué a los mantenedores no les gusta esto.

grep
fuente
En un contexto SQL ordinario, esto no tiene sentido. El objeto de la base de datos solo se puede cambiar después de que finalice su transacción y haga una commmit. Una vez que haya hecho eso, tendría que esperar a que se confirme la próxima transacción SQL. ¿Por qué hacer eso? ¿Cuánto tiempo esperarás para la próxima transacción?
S.Lott
Esto parece una función innecesaria; Ya es posible volver a buscar el objeto desde la base de datos.
Stephan
Me gustaría esto también, pero se ha cerrado repetidamente aquí
Eruciforme
2
No es apropiado porque los objetos modelo de Django son proxies. Si obtiene la misma fila de la tabla en dos objetos: x1 = X.objects.get (id = 1); x2 = X.objects.get (id = 1), se probarán como iguales pero son objetos diferentes y el estado no se comparte. Puede cambiar ambos de forma independiente y guardarlos: el último guardado determina el estado de la fila en la base de datos. Por lo tanto, es correcto recargar con una asignación simple: x1 = X.objects.get (id = 1). Tener un método de recarga llevaría a muchas personas a inferir erróneamente que x1.f = 'nuevo valor'; (x1.f == x2.f) es verdadero.
Paul Whipp

Respuestas:

260

A partir de Django 1.8, los objetos de actualización están integrados. Enlace a documentos .

def test_update_result(self):
    obj = MyModel.objects.create(val=1)
    MyModel.objects.filter(pk=obj.pk).update(val=F('val') + 1)
    # At this point obj.val is still 1, but the value in the database
    # was updated to 2. The object's updated value needs to be reloaded
    # from the database.
    obj.refresh_from_db()
    self.assertEqual(obj.val, 2)
Tim Fletcher
fuente
@ fcracker79 Sí, solo se implementó en 1.8. Para versiones anteriores de Django, es mejor ir con una de las otras respuestas.
Tim Fletcher
1
¿No está seguro de lo que significa "Todos los campos no diferidos actualizados" mencionados en los documentos?
Yunti
1
@Yunti Puede diferir campos o solicitar explícitamente solo un subconjunto de campos y el objeto resultante solo se completará parcialmente. refresh_from_dbsolo actualizará los campos ya rellenados.
301_Moved_Permanently
No se pudieron encontrar detalles en los documentos, pero arroja correctamente una DoesNotExistexcepción si el objeto subyacente se eliminó al llamar refresh_from_db. FYI.
Tim Tisdall
28

Me resultó relativamente fácil recargar el objeto desde la base de datos de esta manera:

x = X.objects.get(id=x.id)
Rory
fuente
19
Sí, pero ... después de eso, debe actualizar todas las referencias a este objeto. No muy práctico y propenso a errores.
grep
2
Descubrí que esto era necesario cuando Celery actualizó mi objeto en la base de datos fuera de django, aparentemente django mantuvo un caché del objeto ya que no tenía idea de que había cambiado.
Bob Spryn
3
de django.db.models.loading import get_model; instancia = get_model (instancia) .objects.get (pk = instancia.pk)
Erik
1
@grep acaba de perder 2 horas escribiendo una prueba para este caso de uso: 1: Inicialice un modelo; 2: Actualice el modelo a través de un formulario; 3: Pruebe que el nuevo valor se actualiza ... Entonces, sí, propenso a errores.
vlad-ardelean
3
Creo que refresh_from_dbresuelve todos estos problemas.
Flimm
16

En referencia al comentario de @ grep, ¿no debería ser posible hacer:

# Put this on your base model (or monkey patch it onto django's Model if that's your thing)
def reload(self):
    new_self = self.__class__.objects.get(pk=self.pk)
    # You may want to clear out the old dict first or perform a selective merge
    self.__dict__.update(new_self.__dict__)

# Use it like this
bar.foo = foo
assert bar.foo.pk is None
foo.save()
foo.reload()
assert bar.foo is foo and bar.foo.pk is not None
Eloff
fuente
Gracias por la solucion. ¡Si solo SO permitiera múltiples votos positivos!
user590028
11
Django ahora proporciona refresh_from_dbmétodo.
Flimm
9

Como señaló @Flimm, esta es una solución realmente increíble:

foo.refresh_from_db()

Esto vuelve a cargar todos los datos de la base de datos en el objeto.

Ron
fuente