Custom fields in Many2Many JoinTable

Viewed 1357

I have this model with a custom JoinTable:

type Person struct {
  ID        int
  Name      string
  Addresses []Address `gorm:"many2many:person_addresses;"`
}

type Address struct {
  ID   uint
  Name string
}

type PersonAddress struct {
  PersonID  int
  AddressID int
  Home      bool
  CreatedAt time.Time
  DeletedAt gorm.DeletedAt
}

How is it possible to assign a value to the Home field when creating a new Person?

2 Answers

Method 1

From what I can see in the docs, here's a clean way you might currently do this:

DB.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})

addr1 := Address{Name: "addr1"}
DB.Create(&addr1)

addr2 := Address{Name: "addr2"}
DB.Create(&addr2)

person := Person{Name: "jinzhu"}
DB.Create(&person)

// Add an association with default values (i.e. Home = false)
DB.Model(&person).Association("Addresses").Append(&addr1)

// Add an association with custom values
DB.Create(&PersonAddress{
    PersonID:  person.ID,
    AddressID: addr2.ID,
    Home:      true,
})

Here we're using the actual join table model to insert a row with the values we want.

We can also filter queries for the association:

addr := Address{}
// Query association with filters on join table
DB.Where("person_addresses.home = true").
    Model(&person).
    Association("Addresses").
    Find(&addr)

Method 2

Here's a more magical way, by (ab)using the Context to pass values to a BeforeSave hook, in addition to the SetupJoinTable code from above:

func (pa *PersonAddress) BeforeSave(tx *gorm.DB) error {
    home, ok := tx.Statement.Context.Value("home").(bool)
    if ok {
        pa.Home = home
    }
    return nil
}

// ...

DB.WithContext(context.WithValue(context.Background(), "home", true)).
    Model(&person).
    Association("Addresses").
    Append(&addr2)

This method feels icky to me, but it works.

As you can find this point in the official documents of the GROM, you can implement some methods for each table(struct).

You can implement BeforeCreate() and/or AfterCreate() methods for your join table, gorm will check that method on time!

You can do anything inside those methods to achieve your goal. here you will find the full documentation.

enjoy ;)

Related