variable template examples django django-models

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.