Defining models
A model is just a struct. It doesn't need a base type, tags, or generated code.
A model
type Article struct {
ID int64
Title string
Views int
}
type BlogDB struct {
ant.Context
Articles ant.Table[Article]
}
var A = ant.Fields[Article]()
That maps to a table called articles, with id as a primary key the database generates.
ant.Base
Embed ant.Base for the three fields most tables want. It's optional, and the fields behave like any others.
type Article struct {
ant.Base
Title string
}
article := &Article{Title: "Hello"}
err := db.Articles.Insert(ctx, article)
fmt.Println(article.ID, article.CreatedAt, article.UpdatedAt) // all filled in
There's nothing special about ant.Base itself: any model with a CreatedAt or UpdatedAt field gets the same timestamps.
Naming conventions
| Go | Database |
|---|---|
type OrderItem | table order_items |
UserID | column user_id |
HTTPPort | column http_port |
ID | primary key id |
ant adds s, or es after s, x, ch, and sh, and turns a consonant and y into ies. That gives categories for Category, but also persons for Person and childs for Child. Use b.Table when that reads wrong.
Overriding names
A tag renames a column, and Configure renames a table.
type Device struct {
ID int64
Name string `ant:"column=display_name"`
}
func (Device) Configure(b *ant.ModelBuilder[Device]) {
b.Table("hardware")
}
Leaving a field out
ant:"-" and unexported fields aren't columns, so they're never read, written, or queried.
type Session struct {
ID int64
Token string
Cached string `ant:"-"`
secret string
}
Your own naming strategy
ant.WithNaming replaces the conventions, which is useful for an existing database with its own style. A nil function keeps the default, and any name you set explicitly stays.
db, err := ant.Open[AppDB](ctx, postgres.Dialect, dsn,
ant.WithNaming(ant.Naming{
Table: func(name string) string { return "tbl_" + name },
Column: func(name string) string { return "col_" + name },
}),
)
ant.WithConvention goes a step further and applies a rule to every model. Configure can still override it.
rule := func(m *ant.ModelConvention) {
for _, f := range m.Fields() {
if f.Type.Kind() == reflect.String {
f.SetLength(100)
}
}
}
db, err := ant.Open[AppDB](ctx, postgres.Dialect, dsn, ant.WithConvention(rule))
Models are reflected once per process, so naming and conventions are global, and they come from the first Open.
See it tested: tests/features/model_definition