examples - import django template
¿Cuál es la manera canónica de averiguar si un modelo de Django se guarda en db? (4)
Generalmente compruebo if obj.pk
sabe si los objetos se guardan. Sin embargo, esto no funcionará si tiene primary_key = True
set en algunos campos. Por ejemplo, establezco user = models.OneToOneField(User, primary_key=True)
en mi UserProfile
.
¿Cuál es la manera canónica de averiguar si un modelo de Django se guarda en db?
Digamos que obj
es una instancia de MyModel
. Entonces podríamos usar el siguiente bloque de código para verificar si ya hay una instancia con esa clave principal en la base de datos:
if obj.pk is None:
# Definitely doesn''t exist, since there''s no `pk`.
exists = False
else:
# The `pk` is set, but it doesn''t guarantee exists in db.
try:
obj_from_db = MyModel.objects.get(pk=obj.pk)
exists = True
except MyModel.DoesNotExist:
exists = False
Esto es mejor que verificar si obj.pk is None
, porque podrías hacer
obj = MyModel()
obj.pk = 123
entonces
obj.pk is None # False
Esto es incluso muy probable cuando no usa el campo de id
autoincrement como la clave principal sino una natural.
O, como Matthew señaló en los comentarios, podrías hacer
obj.delete()
después de lo cual todavía tienes
obj.pk is None # False
En realidad, obj.pk
es la forma más canónica. Django a menudo no "sabe" si el objeto está guardado o no. De acuerdo con la referencia de instancia del modelo django , si ya hay una clave primaria establecida, comprueba las llamadas a save()
seleccionando para el id en la base de datos antes de cualquier inserción.
Incluso si establece user = models.OneToOneField(..., primary_key=True)
el atributo .pk
aún apuntará a la clave principal correcta (muy probablemente user_id
) y puede usarla y establecerla como si fuera la misma propiedad .
Si desea saber después de que se ha guardado un objeto, puede capturar la señal de post_save . Esta señal se activa en el modelo de guardado, y si lo desea, puede agregar su propio atributo específico de la aplicación al modelo, por ejemplo, obj.was_saved = True
. Creo que django evita esto para mantener sus instancias limpias, pero no hay una razón real por la que no puedas hacer esto por ti mismo. Aquí hay un ejemplo mínimo:
from django.db.models.signals import post_save
from myapp.models import MyModel
def save_handler(sender, instance, **kwargs):
instance.was_saved = True
post_save.connect(save_handler, sender=MyModel)
Alternativamente, puede hacer que esta función funcione para todos los modelos en su aplicación simplemente conectando la señal sin especificar el sender=
argumento. Sin embargo, tenga cuidado, puede crear comportamientos indefinidos si anula una propiedad en la instancia de modelo de otra persona que está importando.
Hoy en día puedes verificar:
self._state.adding
Este valor lo establece QuerySet.iterator()
para los objetos que aún no se agregan en la base de datos. No puede usar este valor en el __init__()
aún, ya que se configura después de que se construye el objeto.
La respuesta de @ Crast fue buena, pero creo que está incompleta. El código que uso en mi unidad para determinar si un objeto está en la base de datos es el siguiente. Debajo, explicaré por qué creo que es mejor que verificar si obj.pk is None
.
Mi solución
from django.test import TestCase
class TestCase(TestCase):
def assertInDB(self, obj, msg=None):
"""Test for obj''s presence in the database."""
fullmsg = "Object %r unexpectedly not found in the database" % obj
fullmsg += ": " + msg if msg else ""
try:
type(obj).objects.get(pk=obj.pk)
except obj.DoesNotExist:
self.fail(fullmsg)
def assertNotInDB(self, obj, msg=None):
"""Test for obj''s absence from the database."""
fullmsg = "Object %r unexpectedly found in the database" % obj
fullmsg += ": " + msg if msg else ""
try:
type(obj).objects.get(pk=obj.pk)
except obj.DoesNotExist:
return
else:
self.fail(fullmsg)
Notas: Utilice el código anterior con cuidado si utiliza administradores personalizados en el nombre de su modelo, que no sean objects
. (Estoy seguro de que hay una forma de que Django le diga cuál es el administrador predeterminado). Además, sé que /assert(Not)?InDB/
no son nombres de un método PEP 8, pero utilicé el estilo del resto el paquete unittest
utilizado.
Justificación
La razón por la que creo que assertInDB(obj)
es mejor que assertIsNotNone(obj.pk)
se debe al siguiente caso. Supongamos que tiene el siguiente modelo.
from django.db import models
class Node(models.Model):
next = models.OneToOneField(''self'', null=True, related_name=''prev'')
Node
modela una lista doblemente enlazada: puede adjuntar datos arbitrarios a cada nodo utilizando claves externas y la cola es el Node
Obj tal que obj.next is None
. Por defecto, Django agrega la restricción de SQL ON DELETE CASCADE
a la clave primaria de Node
. Ahora, supongamos que tiene una list
nodos de longitud n tal que los nodes[i].next == nodes[i + 1]
para i en [0, n - 1). Supongamos que llama a los nodes[0].delete()
. En mis pruebas en Django 1.5.1 en Python 3.3, encontré que los nodes[i].pk is not None
para i en [1, n ) y solo los nodes[0].pk is None
. Sin embargo, my /assert(Not)?InDB/
methods above detectó correctamente que los nodes[i]
para i en [1, n ) habían sido eliminados.