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]()
)
| Call | Declares |
|---|---|
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.
See it tested: tests/features/relations