# ORM Model
Beego's ORM module requires that the model is registered before it can be used. Beego performs certain checks to assist in checking the model and the constraints between the models. The definition of models has impact on creating table automatically.
Beego's model definition, which mostly relies on Go tag features, can set multiple features, separated by ;
. Different values of the same feature are separated by ,
.
Example:
orm:"null;rel(fk)"
# Register
There are three ways to register a model:
RegisterModel(models ...interface{})
RegisterModelWithPrefix(prefix string, models ...interface{})
: This method prefixes the table name with,i.e.RegisterModelWithPrefix("tab_", &User{})
=>tab_user
.RegisterModelWithSuffix(suffix string, models ...interface{})
: This method adds a suffix to the table name, i.e.RegisterModelWithSuffix("_tab", &User{})
=>user_tab
.
# Basic Usage
# Table Name
Beego ORM will use the snake case as the table name:
AuthUser -> auth_user
Auth_User -> auth__user
DB_AuthUser -> d_b__auth_user
Or you can specify the table name by implementing interface TableNameI
:
type User struct {
Id int
Name string
}
func (u *User) TableName() string {
return "auth_user"
}
Also, you can add a prefix or suffix to the table name when registering the model. For more details, please refer to the section Registering the Model.
# Column
Using tag to specify the column name:
Name string `orm:"column(user_name)"`
# Ignore Fields
Using tag "-"
to ignore some fields:
type User struct {
// ...
AnyField string `orm:"-"`
//...
}
# Index
Similarly, you can use tag to specify indexes, including unique index.
For example, specify the index for the field:
Name string `orm:"index"`
Or specify the field as unique index:
Name string `orm:"unique"`
Or implement the interface TableIndexI
:
type User struct {
Id int
Name string
Email string
}
// index with multiple columns
func (u *User) TableIndex() [][]string {
return [][]string{
[]string{"Id", "Name"},
}
}
// unique index with columns
func (u *User) TableUnique() [][]string {
return [][]string{
[]string{"Name", "Email"},
}
}
# Primary Key
You can specify a field as a auto-incrementing primary key using the auto
tag. The type of the specific fields must be int, int32, int64, uint, uint32 or uint64.
MyId int32 `orm:"auto"`
If a model does not have a primary key defined, then a field of the above type with the name Id
will be treated as a auto-incrementing primary key.
If you don't want to use a auto-incrementing primary key, then you can use pk
tag to specify the primary key.
Name string `orm:"pk"`
Note that Beego's non-auto-incrementing primary keys and union primary keys are not particularly well supported now. It is recommended to use self-incrementing primary keys in general.
Given go's current design, even though uint64 is used, you can't store it to its maximum value. It will still be treated as int64. For more details, please refer to issue 6113 (opens new window).
# Default Value
You could use default value like that:
import (
"github.com/beego/beego/v2/client/orm/filter/bean"
"github.com/beego/beego/v2/client/orm"
)
type DefaultValueTestEntity struct {
Id int
Age int `default:"12"`
AgeInOldStyle int `orm:"default(13);bee()"`
AgeIgnore int
}
func XXX() {
builder := bean.NewDefaultValueFilterChainBuilder(nil, true, true)
orm.AddGlobalFilterChain(builder.FilterChain)
o := orm.NewOrm()
_, _ = o.Insert(&User{
ID: 1,
Name: "Tom",
})
}
NewDefaultValueFilterChainBuilder
will create an instance of DefaultValueFilterChainBuilder
.
In beego v1.x, the default value config looks like orm:default(xxxx)
.
But the default value in 2.x is default:xxx
, so if you want to be compatible with v1.x, please pass true as compatibleWithOldStyle
.
# auto_now / auto_now_add
Created time.Time `orm:"auto_now_add;type(datetime)"`
Updated time.Time `orm:"auto_now;type(datetime)"`
- auto_now: every save will update time.
- auto_now_add: set time at the first save
This setting won't affect massive update
.
# engine
Only supports MySQL database.
The default engine is the default engine of the current database engine of your mysql settings.
Using TableEngineI
interface:
type User struct {
Id int
Name string
Email string
}
// Set engine to INNODB
func (u *User) TableEngine() string {
return "INNODB"
}
# Advance Usage
# null
Fields are NOT NULL
by default. Set null to ALLOW NULL
.
Name string `orm:"null"`
# size
Default value for string field is varchar(255).
It will use varchar(size) after setting.
Title string `orm:"size(60)"`
# digits / decimals
Set precision for float32 or float64.
Money float64 `orm:"digits(12);decimals(4)"`
Total 12 digits, 4 digits after point. For example: 12345678.1234
# type
If set type as date, the field's db type is date.
Created time.Time `orm:"auto_now_add;type(date)"`
If set type as datetime, the field's db type is datetime.
Created time.Time `orm:"auto_now_add;type(datetime)"`
# Time Precision
type User struct {
...
Created time.Time `orm:"type(datetime);precision(4)"`
...
}
# Comment
type User struct {
...
Status int `orm:"default(1);description(this is status)"`
...
}
You should never use quoter as the value of description.
# Types Mapping
# Model fields mapping with database type
Here is the recommended database type mapping. It's also the standard for table generation.
All the fields are NOT NULL by default.
# MySQL
go | mysql |
---|---|
int, int32 - set as auto or name is Id | integer AUTO_INCREMENT |
int64 - set as auto or name isId | bigint AUTO_INCREMENT |
uint, uint32 - set as auto or name is Id | integer unsigned AUTO_INCREMENT |
uint64 - set as auto or name is Id | bigint unsigned AUTO_INCREMENT |
bool | bool |
string - default size 255 | varchar(size) |
string - set type(char) | char(size) |
string - set type(text) | longtext |
time.Time - set type as date | date |
time.Time | datetime |
byte | tinyint unsigned |
rune | integer |
int | integer |
int8 | tinyint |
int16 | smallint |
int32 | integer |
int64 | bigint |
uint | integer unsigned |
uint8 | tinyint unsigned |
uint16 | smallint unsigned |
uint32 | integer unsigned |
uint64 | bigint unsigned |
float32 | double precision |
float64 | double precision |
float64 - set digits and decimals | numeric(digits, decimals) |
# Sqlite3
go | sqlite3 |
---|---|
int, int32, int64, uint, uint32, uint64 - set as auto or name is Id | integer AUTOINCREMENT |
bool | bool |
string - default size 255 | varchar(size) |
string - set type(char) | character(size) |
string - set type(text) | text |
time.Time - set type as date | date |
time.Time | datetime |
byte | tinyint unsigned |
rune | integer |
int | integer |
int8 | tinyint |
int16 | smallint |
int32 | integer |
int64 | bigint |
uint | integer unsigned |
uint8 | tinyint unsigned |
uint16 | smallint unsigned |
uint32 | integer unsigned |
uint64 | bigint unsigned |
float32 | real |
float64 | real |
float64 - set digits and decimals | decimal |
# PostgreSQL
go | postgres |
---|---|
int, int32, int64, uint, uint32, uint64 - set as auto or name is Id | serial |
bool | bool |
string - if not set size default text | varchar(size) |
string - set type(char) | char(size) |
string - set type(text) | text |
string - set type(json) | json |
string - set type(jsonb) | jsonb |
time.Time - set type as date | date |
time.Time | timestamp with time zone |
byte | smallint CHECK("column" >= 0 AND "column" <= 255) |
rune | integer |
int | integer |
int8 | smallint CHECK("column" >= -127 AND "column" <= 128) |
int16 | smallint |
int32 | integer |
int64 | bigint |
uint | bigint CHECK("column" >= 0) |
uint8 | smallint CHECK("column" >= 0 AND "column" <= 255) |
uint16 | integer CHECK("column" >= 0) |
uint32 | bigint CHECK("column" >= 0) |
uint64 | bigint CHECK("column" >= 0) |
float32 | double precision |
float64 | double precision |
float64 - set digits and decimals | numeric(digits, decimals) |
# Relationships
# rel / reverse
RelOneToOne:
type User struct {
...
Profile *Profile `orm:"null;rel(one);on_delete(set_null)"`
...
}
The reverse relationship RelReverseOne:
type Profile struct {
...
User *User `orm:"reverse(one)"`
...
}
RelForeignKey:
type Post struct {
...
User *User `orm:"rel(fk)"` // RelForeignKey relation
...
}
The reverse relationship RelReverseMany:
type User struct {
...
Posts []*Post `orm:"reverse(many)"` // fk 的反向关系
...
}
RelManyToMany:
type Post struct {
...
Tags []*Tag `orm:"rel(m2m)"` // ManyToMany relation
...
}
The reverse relationship RelReverseMany:
type Tag struct {
...
Posts []*Post `orm:"reverse(many)"`
...
}
# rel_table / rel_through
This setting is for orm:"rel(m2m)"
field:
rel_table Set the auto-generated m2m connecting table name
rel_through If you want to use custom m2m connecting table, set name by using this setting.
Format: `project_path/current_package.ModelName`
For example: `app/models.PostTagRel` PostTagRel table needs to have a relationship to Post table and Tag table.
If rel_table
is set, rel_through
is ignored.
You can set these as follows:
orm:"rel(m2m);rel_table(the_table_name)"
orm:"rel(m2m);rel_through(project_path/current_package.ModelName)"
# on_delete
Set how to deal with field if related relationship is deleted:
cascade cascade delete (default)
set_null set to NULL. Need to set null = true
set_default set to default value. Need to set default value.
do_nothing do nothing. ignore.
type User struct {
...
Profile *Profile `orm:"null;rel(one);on_delete(set_null)"`
...
}
type Profile struct {
...
User *User `orm:"reverse(one)"`
...
}
// Set User.Profile to NULL while deleting Profile
# Example
type User struct {
Id int
Name string
}
type Post struct {
Id int
Title string
User *User `orm:"rel(fk)"`
}
Assume Post -> User
is ManyToOne relationship by foreign key.
o.Filter("Id", 1).Delete()
This will delete User
with Id 1
and all his Posts
.
If you don't want to delete the Posts
, you need to set set_null
type Post struct {
Id int
Title string
User *User `orm:"rel(fk);null;on_delete(set_null)"`
}
In this case, only set related Post.user_id
to NULL
while deleting.
Usually for performance purposes, it doesn't matter to have redundant data. The massive deletion is the real problem.
type Post struct {
Id int
Title string
User *User `orm:"rel(fk);null;on_delete(do_nothing)"`
}
So just don't change Post
(ignore it) while deleting User
.