Skip to main content

Declaring relations

A relation is just a field on the struct. []Post is one-to-many, and *User is many-to-one or one-to-one. Most need no configuration at all, because ant finds the foreign key by name.

By convention​

type Person struct {
ID int64
Name string
Passport *Passport // one-to-one, through Passport.PersonID
Pets []Pet // one-to-many, through Pet.PersonID
}

type Passport struct {
ID int64
PersonID int64
Number string
}

type Pet struct {
ID int64
PersonID int64
Name string
Person *Person // many-to-one, through Pet.PersonID
}

Relation fields aren't columns, and a query leaves them empty until you load them. You can declare a relation on one side only.

Configuring a relation​

When names can't find the key, Configure names it. Either side can do this.

type Author struct {
ID int64
Handle string
Books []Book
}

func (Author) Configure(b *ant.ModelBuilder[Author]) {
b.HasMany(&Au.Books).ForeignKey(&Bk.WriterID).OnDelete(ant.Cascade)
}

type Book struct {
ID int64
WriterID int64
Title string
Writer *Author
}

func (Book) Configure(b *ant.ModelBuilder[Book]) {
b.BelongsTo(&Bk.Writer).ForeignKey(&Bk.WriterID)
}

var (
Au = ant.Fields[Author]()
Bk = ant.Fields[Book]()
)
CallDeclares
b.HasMany(&U.Posts)one-to-many
b.HasOne(&U.Passport)one-to-one, key on the other side
b.BelongsTo(&P.User)many-to-one, key on this side
b.ManyToMany(&U.Roles)through a join table

A relation nothing connects returns ant.ErrNoForeignKey at the terminal call.

A key other than the primary key​

References points at another column, which must be unique.

type Quote struct {
ID int64
AuthorHandle string
Text string
}

func (Author) Configure(b *ant.ModelBuilder[Author]) {
b.HasMany(&Au.Quotes).ForeignKey(&Qt.AuthorHandle).References(&Au.Handle)
}

var Qt = ant.Fields[Quote]()

What happens on delete​

Delete behavior is NO ACTION unless you set it, so nothing is removed that you didn't ask for.

func (Post) Configure(b *ant.ModelBuilder[Post]) {
b.BelongsTo(&P.User).OnDelete(ant.Cascade).OnUpdate(ant.NoAction)
}

The actions are ant.Cascade, ant.SetNull, ant.Restrict, and ant.NoAction. They become part of the foreign key in migrations, and the database enforces them.

Other relation options​

func (Post) Configure(b *ant.ModelBuilder[Post]) {
b.BelongsTo(&P.User).NoForeignKey() // keep the relation, skip the constraint
b.BelongsTo(&P.User).NoIndex() // skip the automatic index
}

A model that points at itself​

The parent is found through ParentID, and the children through the matching back-reference.

type Folder struct {
ID int64
ParentID *int64
Parent *Folder
Children []Folder
Name string
}

Use a pointer key, so a root can have no parent.