2009-10-30 9 views
39

Tengo dos modelos en diferentes aplicaciones: modeloA y modeloB. Ellos tienen una relación de uno a uno. ¿Hay alguna manera de que django pueda crear y guardar automáticamente ModelB cuando se guarda el modelo A?¿Puede Django crear automáticamente un modelo relacionado de uno a uno?

class ModelA(models.Model): 
    name = models.CharField(max_length=30) 

class ModelB(models.Model): 
    thing = models.OneToOneField(ModelA, primary_key=True) 
    num_widgets = IntegerField(default=0) 

Cuando guardo un nuevo ModelA, deseo una entrada para que se guarde automáticamente en ModelB. ¿Cómo puedo hacer esto? ¿Hay alguna manera de especificar eso en ModelA? ¿O esto no es posible, y solo necesitaría crear y guardar ModelB en la vista?

Editado para decir que los modelos se encuentran en diferentes aplicaciones.

+1

Posible duplicado de [Crear instancia OneToOne en la creación del modelo] (http://stackoverflow.com/questions/5608001/create-onetoone-instance-on-model-creation) –

Respuesta

0

Puede usar el post_save-hook que se activa después de guardar un registro. Para obtener más documentación sobre las señales de django, consulte here. En this page, encontrará un ejemplo sobre cómo aplicar el gancho en su modelo.

13

La forma más sencilla consiste en override the save method de ModelA:

class ModelA(models.Model): 
    name = models.CharField(max_length=30) 

    def save(self, force_insert=False, force_update=False): 
     is_new = self.id is None 
     super(ModelA, self).save(force_insert, force_update) 
     if is_new: 
      ModelB.objects.create(thing=self) 
+0

El problema de hacerlo de esta manera es que lamentablemente se rompe si tiene una forma en línea en el administrador y la usa para crear una instancia de ModelB al mismo tiempo; intentará crear dos ModelB y morirá horriblemente. –

+0

Sí, pero consideraría esto un truco. – Dmitry

+3

Podría querer ser más a prueba de futuro al no nombrar los argumentos a súper. Sugeriré una edición. – hobs

36

Tome un vistazo a la AutoOneToOneField en django-annoying. A partir de los documentos:

from annoying.fields import AutoOneToOneField 

class MyProfile(models.Model): 
    user = AutoOneToOneField(User, primary_key=True) 
    home_page = models.URLField(max_length=255) 
    icq = models.CharField(max_length=255) 

(django-molesto es un pequeño gran biblioteca que incluye joyas como el decorador render_to y la get_object_or_None y funciones GET_CONFIG)

+0

Vale la pena señalar que la creación de un nuevo usuario en el panel de administración no creará MyProfile de inmediato. Se ha creado de forma perezosa (la primera vez que accede a ese objeto de perfil). – BUZZY

1

Creo que desee utilizar django's model inheritance. Esto es útil si la siguiente afirmación es verdadera: ModelA es un ModelB (como, el restaurante es una ubicación).

Puede definir:

class ModelB(models.Model): 
    field1 = models.CharField(...)

class ModelA(ModelB): field2 = models.CharField(...)

Now you can create an instance of ModelA and set field2 and field1. If this model is saved it will also create an instance of ModelB which gets the value of field1 assigned. This is all done transparently behind the scenes.

Though you can do the following:

a1 = ModelA() 
a1.field1 = "foo" 
a1.field2 = "bar" 
a1.save() 
a2 = ModelA.objects.get(id=a1.id) 
a2.field1 == "foo" # is True 
a2.field2 == "bar" # is True 
b1 = ModelB.objects.get(id=a1.id) 
b1.field1 == "foo" # is True 
# b1.field2 is not defined

0

Basta con crear una función que crea y devuelve un ModelA vacío, y establecer el argumento con nombre por defecto en "cosa" a esa función.

23

Al igual que m000, sus modelos existen en diferentes aplicaciones. A menudo utiliza aplicaciones que no escribió, por lo que para permitir actualizaciones necesita una forma desacoplada para crear modelos lógicamente relacionados. Esta es la solución preferida en mi opinión y la usamos en un proyecto muy grande.

mediante el uso de señales:

En su models.py:

from django.db.models import signals 


def create_model_b(sender, instance, created, **kwargs): 
    """Create ModelB for every new ModelA.""" 
    if created: 
     ModelB.objects.create(thing=instance) 

signals.post_save.connect(create_model_b, sender=ModelA, weak=False, 
          dispatch_uid='models.create_model_b') 

Puede crear una aplicación separada para mantener este archivo models.py si tanto de las aplicaciones son off-the-shelf.

+1

+1 para esto. El problema en la pregunta es que los modelos pertenecen a diferentes aplicaciones. Esto coincide con el caso de uso de las señales: "permitir que las aplicaciones desacopladas sean notificadas cuando las acciones ocurran en otro lugar en el marco". Otras soluciones propuestas funcionan, pero introducen una dependencia innecesaria A-> B, que agrupa esencialmente las dos aplicaciones. Las señales permiten que A permanezca desacoplado de B. – m000

+0

@ m000 ¡Gracias por esto! Si no te importa, actualizaré la descripción de mi solución, ya que la has resumido muy bien. – Dmitry

+0

¿Por qué 'weak = False'? –

1

Sé que es un poco tarde, pero se me ocurrió una solución más limpia y más elegante. Considere este código:

class ModelA(models.Model): 
    name = models.CharField(max_length=30) 

    @classmethod 
    def get_new(cls): 
     return cls.objects.create().id 



class ModelB(models.Model): 
    thing = models.OneToOneField(ModelA, primary_key=True, default=ModelA.get_new) 
    num_widgets = IntegerField(default=0) 

Por supuesto se puede utilizar lambda así, siempre y cuando regrese Identificación del número entero de objeto relacionado :)

0

Reuní algunas respuestas diferentes (porque ninguno de ellos trabajó recta fuera de la caja para mí) y se le ocurrió esto. Pensé que estaba bastante limpio, así que lo estoy compartiendo.

from django.db.models.signals import post_save 
from django.dispatch import receiver 

@receiver(post_save, sender=ModelA) 
def create_modelb(sender, instance, created, **kwargs): 
    if created: 
     if not hasattr(instance, 'modelb'): 
      ModelB.objects.create(thing=instance) 

Está utilizando Signal como @Dmitry sugirió.Y como @ daniel-roseman comentó en la respuesta de @ jarret-hardie, Django Admin intenta crear el objeto relacionado para usted a veces (si cambia el valor predeterminado en la forma en línea), con el que me encontré, por lo tanto, la comprobación hasattr. La bonita sugerencia del decorador es la respuesta de @ shadfc en Create OneToOne instance on model creation

Cuestiones relacionadas