RuNpiXelruN
RuNpiXelruN

Reputation: 1920

Go GORM many2many issue

I can't work out the best way to add an association to a model. I have the following structs

type Beer struct {
        ID             uint       `json:"id"`
    Name           string     `json:"name" gorm:"not null;" sql:"unique"`
    Description    string     `json:"description" gorm:"not null;"`
    ImageURL       string     `json:"image_url"`
    AlcoholContent float64    `json:"alcohol_content, default:0"`
    Featured       bool       `json:"featured"`
    BrewStart      time.Time  `json:"brew_start"`
    BrewEnd        time.Time  `json:"brew_end"` 
    Brewers        []Brewer   `gorm:"many2many:beer_brewers" json:"brewers"`
}

type Brewer struct {
    ID        uint       `json:"id"`
    FirstName string     `json:"first_name"`
    LastName  string     `json:"last_name"`
    Title     string     `json:"title"`
    Featured  bool       `json:"featured"`
    Beers     []Beer    `gorm:"many2many:beer_brewers" json:"beers"`
}

Below is an example of data i have seeded the DB with

Beer{
    Name:           "some pale ale",
    Description:    "a description of some pale ale",
    ImageURL:       "http://via.placeholder.com/350x150",
    AlcoholContent: 4.5,
    Featured:       false,
    BrewStart:      utils.ParseTime("30-10-2017 13:00 (AEDT)"),
    BrewEnd:        utils.ParseTime("14-11-2017 13:00 (AEDT)"),
    Brewers: []Brewer{
        Brewer{FirstName: "john", LastName: "smith", Title: "bottle shaker", Featured: false},
        Brewer{FirstName: "joe", LastName: "bloggs", Title: "bottle maker", Featured: true},
    },
},
Beer{
    Name:           "some lager",
    Description:    "a description of some pale ale",
    ImageURL:       "http://via.placeholder.com/350x150",
    AlcoholContent: 4.5,
    Featured:       false,
    BrewStart:      utils.ParseTime("30-10-2017 13:00 (AEDT)"),
    BrewEnd:        utils.ParseTime("14-11-2017 13:00 (AEDT)"),
    Brewers: []Brewer{
        Brewer{FirstName: "john", LastName: "smith", Title: "bottle shaker", Featured: false},
        Brewer{FirstName: "joe", LastName: "bloggs", Title: "bottle maker", Featured: true},
    },
},

However the above creates duplicate Brewers in the Brewer table. My question is, what is the best way to reference a Brewer that already exists but not create another Brewer item in the Brewer table?..and also what is the best way to Append a new Brewer into a Beer collection?

Thanks, Justin

Upvotes: 4

Views: 5983

Answers (4)

Rami H
Rami H

Reputation: 144

The idea here is the following 1. You create the brewers independently 2. When you append them, add the primary key field "BrewerId" as shown 3. This will lookup the Brewer in the brewers table and add it to the Beer.

import (
    "time"

    "github.com/jinzhu/gorm"

    // used by gorm
    _ "github.com/jinzhu/gorm/dialects/postgres"
)

type Beer struct {
    BeerId         uint      `json:"id"`
    Name           string    `json:"name" gorm:"not null;" sql:"unique"`
    Description    string    `json:"description" gorm:"not null;"`
    ImageURL       string    `json:"image_url"`
    AlcoholContent float64   `json:"alcohol_content, default:0"`
    Featured       bool      `json:"featured"`
    BrewStart      time.Time `json:"brew_start"`
    BrewEnd        time.Time `json:"brew_end"`
    Brewers        []Brewer  `json:"brewers" gorm:"many2many:beer_brewers;association_foreignkey:brewer_id;foreignkey:beer_id"`
}

type Brewer struct {
    BrewerId  uint   `json:"id"`
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
    Title     string `json:"title"`
    Featured  bool   `json:"featured"`
}

func migrate() {
    // Connection is the connection string
    connection := "host=%v port=%v user=%v dbname=%v password=%v sslmode=%v connect_timeout=%v"
    db, _ := gorm.Open("postgres", connection)
    db.AutoMigrate(&Beer{}, &Brewer{})
    db.Model(&Beer{}).Related(&Brewer{}, "Brewers")

    db.Create(&Brewer{FirstName: "justin"})
    db.Create(&Beer{
        Name:           "some lager",
    Description:    "a description of some pale ale",
    ImageURL:       "http://via.placeholder.com/350x150",
    AlcoholContent: 4.5,
    Featured:       false,
    BrewStart:      utils.ParseTime("30-10-2017 13:00 (AEDT)"),
    BrewEnd:        utils.ParseTime("14-11-2017 13:00 (AEDT)"),
    }).Association("Brewers").Append(&Brewer{BrewerId: 123,})
}

Upvotes: 2

RuNpiXelruN
RuNpiXelruN

Reputation: 1920

So i have figured out a solution to what i wanted to do above. To add a new Brewer into a Beer.Brewers collection I needed to do the below,

brewerr := Brewer{}
    db.Where(&Brewer{FirstName: "justin"}).Find(&brewerr)

beerr := Beer{}
    db.Preload("Brewers").Where(&Beer{Name: "some lager"}).Find(&beerr).Association("Brewers").Append(&brewerr)

It's important to note that I needed to Preload the Beer.Brewers collection first and THEN append. Failing to Preload resulted in the Append overwriting all of the Brewers for that Beer

Upvotes: 1

Ravi R
Ravi R

Reputation: 1782

Not sure if you have considered this as an option - better way may be to hold a reference to (a slice of) Brewer IDs in your Beers.Brewers field. As the data is encoded, you can convert these ID's into full field values (using a customized marshal function). This may be suitable, unless you have speed/performance considerations.

Upvotes: 0

pedropinheiro75
pedropinheiro75

Reputation: 116

Try create first beer, then the brewers and usage the .append method to append the brewers in beer.

http://jinzhu.me/gorm/associations.html#association-mode

// Start Association Mode
var user User
db.Model(&user).Association("Languages")
// `user` is the source, it need to be a valid record (contains primary key)
// `Languages` is source's field name for a relationship.
// If those conditions not matched, will return an error, check it with:
// db.Model(&user).Association("Languages").Error


// Query - Find out all related associations
db.Model(&user).Association("Languages").Find(&languages)


// Append - Append new associations for many2many, has_many, will replace current association for has_one, belongs_to
db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Append(Language{Name: "DE"})

Upvotes: 2

Related Questions