2 votes

Gorm crée un doublon dans l'association

J'ai les 2 structs suivants avec une relation many-2-many.

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"`
}

Si je crée un message avec DB.Create(my_message), tout fonctionne bien : le message est créé dans la base de données, ainsi qu'un emplacement et la table jointe message_locations est remplie avec les ID respectifs du message et de l'emplacement.

Je m'attendais à ce que si le lieu existe déjà dans la base de données (d'après le champ place_id, qui est transmis), gorm crée le message, récupère l'ID du lieu et remplit message_locations. Comme le PlaceID doit être unique, gorm constate qu'une valeur de clé dupliquée viole la contrainte unique "locations_place_id_key" et interrompt la transaction.

Si, par contre, je fais en sorte que le PlaceID ne soit pas unique, gorm crée le message correctement, avec l'association, mais cela crée une autre entrée en double pour le lieu.

Je peux vérifier si l'emplacement existe déjà avant d'essayer d'enregistrer le message :

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

alors si c'est vrai, éteignez l'association :

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

Le message est sauvegardé sans que gorm ne se plaigne, mais alors message_locations n'est pas rempli. Je pourrais le remplir "manuellement" puisque j'ai récupéré l'ID de l'emplacement lors du test de son existence, mais il me semble que cela va à l'encontre de l'objectif d'utiliser gorm en premier lieu.

Je ne suis pas sûr de la façon dont il faut procéder. Il se peut que je rate quelque chose d'évident, je soupçonne qu'il y a peut-être un problème dans la façon dont j'ai déclaré mes structs ? Les conseils sont les bienvenus.

MISE À JOUR 2016/03/25

J'ai fini par faire ce qui suit, ce qui, j'en suis sûr, n'est pas optimal. Si vous avez une meilleure idée, s'il vous plaît chime in.

Après avoir vérifié si l'emplacement existe déjà et c'est le cas :

// 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()

0voto

Guest Points 1

gorm:"many2many:message_locations;save_association:false"

se rapproche de ce que vous aimeriez avoir. Vous devez le placer dans votre définition de structure pour le message. Le champ est alors supposé exister dans la base de données et seule la table des associations sera alimentée en données.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X