2009-04-06 24 views
1

Ich habe eine Blog-ähnliche Anwendung mit Geschichten und Kategorien:Einfügen Objekt mit ManyToMany in Django

class Category(models.Model): 
    ... 
class Story(models.Model): 
    categories = models.ManyToManyField(Category) 
    ... 

Jetzt weiß ich, dass, wenn Sie mit einem many-to-many-Feld, eine neue Instanz eines Modell speichern Probleme entstehen, weil das Objekt noch nicht in der Datenbank ist. Dieses Problem manifestiert sich normalerweise bei der Formularübergabe, die mit story_form.save(commit=False) sauber bearbeitet werden kann. Was ist mit einer Situation, in der es keine Formen zu sprechen gibt? In meinem Fall möchte ich eine API erstellen, um Remote-Übertragungen zu akzeptieren. Da ich JSON mag, und eine ganze Menge anderer Messaging in unserem Unternehmen ist in JSON (einschließlich ausgehenden Nachrichten von diesem Server), würde Ich mag Lage sein, die folgend erhalten:

{ "operation": "INSERT", 
    "values": [ 
      { "datatype": "story", 
       "categories": [4,6,8], 
       "id":50, 
       ... 
      } 
      ] 
} 

und eine Fabrik implementieren Das konvertiert die Werte in Instanzen. Aber ich möchte, dass die Fabrik so agnostisch wie möglich für die Art der Operation ist. Also:

{ "operation": "UPDATE", 
    "values": [ 
      { "datatype": "story", 
       "categories": [4,6,8], 
       "id":50, 
       ... 
      } 
      ] 
} 

soll auch auf die gleiche Weise umgewandelt werden, mit der Ausnahme, dass INSERT ignoriert id und UPDATE wird die bereits vorhandene Instanz und außer Kraft gesetzt. (Der Remote-Übermittler hört auf einen Feed, der unter anderem die zu cachenden Kategorieobjekte bereitstellt. Er kann und muss sich daher auf diese per ID beziehen, hat jedoch keine direkte Kommunikation mit der Datenbank.)

Meine eigentliche Frage ist: Was ist die einfachste Konsistenz, um eine Instanz eines Django-Modellobjekts aufzublasen, an dem ein ManyToManyManager beteiligt ist. Soweit ich es ergründen kann, erfordert jedes Einfügen eines Objekts mit einem Viele-zu-Viele-Feld zwei Datenbanktreffer, nur weil es notwendig ist, zuerst eine neue ID zu erhalten. Aber meine derzeitige peinliche Lösung besteht darin, das Objekt sofort zu speichern und es versteckt zu markieren, damit Funktionen auf der ganzen Linie damit spielen und es als etwas aussagekräftiger speichern können. Es scheint so, als würde ein Schritt save überschreiben, so dass Objekte ohne IDs einmal speichern, ein Proxy-Feld in categories kopieren und dann erneut speichern. Das Beste von allem wäre ein robustes Manager-Objekt, das mir die Probleme erspart. Was empfehlen Sie?

Antwort

2

Ich habe S.Lott's Beitrag kommentiert, dass ich denke, dass seine Antwort die beste ist. Er hat recht: Wenn das Ziel nur darin besteht, zwei Datenbanktreffer zu vermeiden, dann sind Sie einfach in einer Welt unnötiger Schmerzen.

Lesen Ihrer Bezug auf Modelform, jedoch, wenn Sie stattdessen nach einer Lösung suchen, um das Ihnen erlaubt, offizielle Einsparung in irgendeiner Weise zu verschieben, können Sie einen Blick auf die save_instance() Funktion in forms.models haben möchten. Die innere Funktion save_m2m ist, wie die verzögerte Viele-zu-Viele-Sicherung für Formulare durchgeführt wird. Die Umsetzung von etwas für Modelle ohne Formen würde grundsätzlich dem gleichen Prinzip folgen.

Nachdem gesagt, und zurück zu S.Lott's Post, der Fall eines ModelForm und eines tatsächlichen Modells sind etwas anders. Da Formulare nur einen "sicheren" Satz von Daten offenlegen, die in einem Browser bearbeitet werden sollen ("sicher", weil sie in irgendeiner Weise gefiltert werden, oder kritische Felder ausschließt, die ein Benutzer nicht bearbeiten sollte), ist dies eine vernünftige Designerwartung Möglicherweise muss jemand vor dem Speichern wichtige Informationen zum Formular-abgeleiteten Modell hinzufügen. Deshalb hat Django die commit=False.

Diese Erwartung fällt für Fälle, in denen Sie Modelle direkt instanziieren, herunter. Hier haben Sie programmatischen Zugriff auf die Modell-API, so dass Sie wahrscheinlich feststellen werden, dass die direkte Verwendung dieser API einfacher zu verwalten ist und weniger fehleranfällig ist als durch generalisierte Indirektion. Ich kann verstehen, warum Sie sich das Fabrikkonzept vorstellen, aber in diesem Fall finden Sie vielleicht die Anstrengung, eine kugelsichere Verallgemeinerung für alle Arten von Modellen zu erstellen, eine Komplikation, die sich einfach nicht lohnt.

+0

Ich mag das mehr oder weniger. Ich denke immer noch, dass es Unsinn ist, dass man vor dem Speichern nicht auf ein m2m-Feld zugreifen kann. Das beabsichtigte Verhalten ist offensichtlich. –

3

„Soweit ich ergründen, jede Einlage eines Objekts mit einer many-to-many-Feld werden zwei Datenbanktreffer erfordern, ...“

So what?

Micromanaging jeder einzelne Datenbankzugriff ist in der Regel nicht das ganze Denken wert. Mach die einfachste und offensichtlichste Sache, damit Django den Cache für dich optimieren kann.

Ihre Anwendungsleistung ist --typisch- durch den langsamen Download in den Browser und alle JPEGS, CSS und andere statische Inhalte, die Teil Ihrer Seite sind, dominiert.

Die Zeit, die in Gehirn-Krämpfen verbracht wird, darüber nachzudenken, wie man zwei Primärschlüssel (für eine Viele-zu-Viele-Beziehung) ohne zwei Datenbankzugriffe macht, wird sich nicht gut auszahlen. Zwei PKs sind normalerweise zwei Datenbankzugriffe.


bearbeiten

"... Würfe die Datenbank auf Fehler ..."

Django Transaktionen hat. Siehe http://docs.djangoproject.com/en/dev/topics/db/transactions/#managing-database-transactions. Verwenden Sie den Dekorateur @transaction.commit_manually.

„Kräfte Validierung, die später auftreten gemeint ist“

macht keinen Sinn machen - aktualisieren Sie Ihre Frage, dies zu erklären.

+0

+1 für die Arbeit in der Phrase "Gehirn-Krämpfe". In aller Ernsthaftigkeit, +1, um darauf hinzuweisen, dass sich das Managen der DB wahrscheinlich nicht lohnt. Die Verwendung von Djangos ORM bringt zu viele Vorteile mit sich, um in einem Fall wie diesem über Minuzien nachzudenken. –

+1

Ich stimme zu, dass es sich nicht lohnt, sich über zwei Datenbanktreffer Gedanken zu machen. (Ich glaube nicht einmal, dass ich es mit roher SQL besser machen könnte.) Aber ich möchte nicht früh sparen, nur um meine Implementierung funktionieren zu lassen. Es wirft die Datenbank auf Fehler und erzwingt Validierung, die später auftreten soll. –

Verwandte Themen