2016-05-22 11 views
2

Ich habe die folgenden 2 Strukturen mit einer Viele-2-Viele-Beziehung.Gorm erstellt doppelte Verknüpfung

type Message struct { 
    gorm.Model 
    Body   string  `tag:"body" schema:"body"` 
    Locations []Location `tag:"locations" gorm:"many2many:message_locations;"` 
    TimeSent  time.Time `tag:"timesent"` 
    TimeReceived time.Time `tag:"timereceived"` 
    User   User 
} 

type Location struct { 
    gorm.Model 
    PlaceID string `tag:"loc_id" gorm:"unique"` 
    Lat  float64 `tag:"loc_lat"` 
    Lng  float64 `tag:"loc_lng"` 
} 

Wenn ich eine Nachricht mit DB.Create (my_message) erstellen, funktioniert alles einwandfrei: die Nachricht in DB erstellt wird, zusammen mit einem Ort und die message_locations Join-Tabelle mit dem jeweiligen IDs der Nachricht gefüllt ist und der Standort.

Ich erwartete zwar, dass, wenn der Standort bereits in der Datenbank existiert (basierend auf dem Feld place_id, das übergeben wird), würde Gorm die Nachricht erstellen, die Standort-ID abrufen und message_locations füllen. Das ist nicht was passiert . Da die PlaceID eindeutig sein muss, findet gorm, dass ein doppelter Schlüsselwert die eindeutige Integritätsbedingung "locations_place_id_key" verletzt und die Transaktion abbricht.

Wenn andererseits die PlaceID nicht eindeutig ist, erstellt gorm die Nachricht in Ordnung, mit der Zuordnung, aber das erstellt dann einen anderen, doppelten Eintrag für den Standort.

Ich kann testen, ob der Standort bereits vorhanden ist, bevor Sie versuchen, die Nachricht zu speichern:

existsLoc := Location{} 
DB.Where("place_id = ?", mssg.Locations[0].PlaceID).First(&existsLoc) 

dann, wenn wahr Schalter der Verband aus:

DB.Set("gorm:save_associations", false).Create(mssg) 
DB.Create(mssg) 

Die Nachricht gespeichert wird, ohne gorm beschweren, aber dann ist message_locations nicht gefüllt. Ich könnte es "manuell" ausfüllen, da ich die Standort-ID beim Testen auf seine Existenz abgerufen habe, aber es scheint mir, dass es den Zweck der Verwendung von Gorm in erster Linie vereitelt.

Ich bin mir nicht sicher, was der richtige Weg zu gehen könnte. Ich könnte etwas Offensichtliches vermissen, ich vermute, vielleicht stimmt etwas nicht mit der Art und Weise, wie ich meine Strukturen deklariert habe? Hinweise willkommen.

UPDATE 2016/03/25

landete ich Sie folgendermaßen vorgehen, den ich bin ziemlich sicher nicht optimal ist. Wenn Sie eine bessere Idee haben, bitte läuten

Nach der Prüfung, ob bereits der Standort existiert, und es tut.

// in a transaction 
tx := DB.Begin() 

// create the message with transaction disabled 
if errMssgCreate := tx.Set("gorm:save_associations", false).Create(mssg).Error; errMssgCreate != nil { 
      tx.Rollback() 
      log.Println(errMssgCreate) 
} 

// then create the association with existing location 
if errMssgLocCreate := tx.Model(&mssg).Association("Locations").Replace(&existLoc).Error; errMssgLocCreate != nil { 
      tx.Rollback() 
      log.Println(errMssgLocCreate) 
} 

tx.Commit() 

Antwort

0

gorm:"many2many:message_locations;save_association:false"

rückt näher zu dem, was Sie haben möchten. Sie müssen es in Ihre Strukturdefinition für Nachricht einfügen. Es wird dann angenommen, dass das Feld in der Datenbank existiert, und nur die Assoziationstabelle wird mit Daten gefüllt.

+0

Ja, ich dachte darüber nach. Statt 'DB.Set (" gorm: save_associations ", false)' von Fall zu Fall. Aber es scheint, als wäre es ein halb volles Glas statt halb leer. Denn dann nimmt Gorm immer wieder an, dass das Feld existiert, aber ich kann mich nicht darauf verlassen. Jemand anderes hat ein [Problem auf dem gorm Repo] (https://github.com/jinzhu/gorm/issues/1061) dafür gemacht. Es ist offen. – nicomo