2009-10-30 12 views
39

Ich habe zwei Modelle in verschiedenen Apps: ModelA und ModelB. Sie haben eine Eins-zu-eins-Beziehung. Gibt es einen Weg, wie Django ModelB automatisch erstellen und speichern kann, wenn ModelA gespeichert wird?Kann Django automatisch ein verwandtes Eins-zu-Eins-Modell erstellen?

Wenn ich ein neues ModelA speichere, möchte ich einen Eintrag, damit es automatisch in ModelB gespeichert wird. Wie kann ich das machen? Gibt es eine Möglichkeit, das in ModelA anzugeben? Oder ist das nicht möglich, und ich würde ModelB nur in der Ansicht erstellen und speichern müssen?

Bearbeitet, um zu sagen, dass die Modelle in verschiedenen Apps sind.

+1

Mögliches Duplikat von [Create OneToOne-Instanz bei Modellerstellung] (http://stackoverflow.com/questions/5608001/create-onetoone-instance-on-model-creation) –

Antwort

0

Sie könnten die post_save-hook verwenden, die ausgelöst wird, nachdem ein Datensatz gespeichert wurde. Weitere Informationen zu Djangosignalen finden Sie unter here. Unter this page finden Sie ein Beispiel, wie Sie den Hook auf Ihr Modell anwenden können.

13

Der einfachste Weg ist, um override the save method von 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

Das Problem damit ist, dass es leider kaputt geht, wenn du ein Inline-Formular im Admin hast und damit gleichzeitig eine ModelB-Instanz erstellst - es wird versuchen, zwei ModelBs zu erstellen und schrecklich zu sterben. –

+0

Yup, aber ich würde dies als einen Hack betrachten. – Dmitry

+3

Vielleicht wollen Sie zukunftssicherer sein, indem Sie die Argumente nicht zu super benennen. Ich schlage einen Schnitt vor. – hobs

36

Werfen Sie einen Blick auf die AutoOneToOneField in django-annoying. Aus der Dokumentation:

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-ärgerlich ist eine große kleine Bibliothek, die Edelsteine ​​wie die render_to Dekorateur und der get_object_or_None und get_config Funktionen enthält)

+0

Es ist erwähnenswert, dass das Erstellen eines neuen Benutzers im Admin-Panel MyProfile nicht sofort erstellt. Es wird auf faule Weise erstellt (das erste Mal, wenn Sie auf dieses Profilobjekt zugreifen). – BUZZY

1

Ich glaube, Sie django's model inheritance verwenden möchten. Dies ist nützlich, wenn Sie die folgende Aussage zutreffen: ModelA ist ein ModelB (wie, Restaurant ist ein Ort).

können Sie definieren:

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

einfach eine Funktion erstellen, die eine leere ModelA erstellt und zurückgibt, und das standardmäßige benannte Argument setzen auf „Dinge“ zu dieser Funktion.

23

Wie m000 darauf hingewiesen, existieren Ihre Modelle in verschiedenen Apps. Häufig verwenden Sie Apps, die Sie nicht geschrieben haben. Um Updates zu ermöglichen, benötigen Sie daher eine entkoppelte Möglichkeit, logisch verwandte Modelle zu erstellen. Dies ist meiner Meinung nach die bevorzugte Lösung und wir verwenden sie in einem sehr großen Projekt.

Durch die Verwendung von Signalen:

In Ihrem 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') 

Sie können eine separate App erstellen, um diese models.py Datei zu halten, wenn sowohl die Anwendungen off-the-shelf sind.

+1

+1 dafür. Der Haken an der Frage ist, dass die Modelle zu verschiedenen Apps gehören. Dies entspricht dem Anwendungsfall für Signale: "Entkoppelte Anwendungen können benachrichtigt werden, wenn Aktionen an anderer Stelle im Framework ausgeführt werden". Andere vorgeschlagene Lösungen funktionieren, führen aber eine unnötige A-> B-Abhängigkeit ein und bündeln im Wesentlichen die beiden Apps. Signale erlaubt A von B getrennt zu bleiben. – m000

+0

@ m000 Danke dafür! Wenn es Ihnen nichts ausmacht, werde ich die Beschreibung meiner Lösung aktualisieren, wie Sie es sehr schön zusammengefasst haben. – Dmitry

+0

Warum 'schwach = falsch '? –

1

Ich weiß, es ist ein bisschen spät, aber ich kam mit einer saubereren und eleganteren Lösung. Betrachten Sie diesen Code:

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) 

Natürlich kann man Lambda als auch nutzen kann, solange Sie integer ID verwandten Objekt zurück :)

0

ich ein paar verschiedene Antworten zusammengesetzt (weil keiner von ihnen gerade gearbeitet out of the box für mich) und kam auf diese. Dachte, es ist ziemlich sauber, also teile ich es.

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) 

Es verwendet Signal als @ Dmitry vorgeschlagen.Und wie @ daniel-roseman in @ Jarret-Hardies Antwort kommentierte, versucht Django Admin manchmal, das verwandte Objekt für dich zu erstellen (wenn du den Standardwert im Inline-Formular änderst), in dem ich gelandet bin, also den hasattr-Check. Der nette Dekorierertipp stammt aus @ safdfs Antwort in Create OneToOne instance on model creation

Verwandte Themen