# 模型定義
## 模型定義
模型一般基于 Go 的基本數據類型、實現了 [Scanner](https://pkg.go.dev/database/sql/sql#Scanner) 和 [Valuer](https://pkg.go.dev/database/sql/driver#Valuer) 接口的自定義類型以及它們的指針/別名
例如:
```
type User struct {
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
}
```
## 約定
GORM 傾向于約定,而不是配置。默認情況下,GORM 使用 `ID` 作為主鍵,使用結構體名的 `蛇形復數` 作為表名,字段名的 `蛇形` 作為列名,并使用 `CreatedAt`、`UpdatedAt` 字段追蹤創建、更新時間
遵循 GORM 已有的約定,可以減少您的配置和代碼量。如果約定不符合您的需求,[GORM 允許您自定義配置它們](../教程/conventions.md)
## gorm.Model
GORM 定義一個 `gorm.Model` 結構體,其包括字段 `ID`、`CreatedAt`、`UpdatedAt`、`DeletedAt`
```go
// gorm.Model 的定義
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
```
您可以將它嵌入到您的結構體中,以包含這幾個字段,詳情請參考 [嵌入結構體](#嵌入結構體)
```go
type User struct {
gorm.Model
Name string
}
// 等效于
type User struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
}
```
## 高級選項
### 字段級權限控制
可導出的字段在使用 GORM 進行 CRUD 時擁有全部的權限,此外,GORM 允許您用標簽控制字段級別的權限。這樣您就可以讓一個字段的權限是只讀、只寫、只創建、只更新或者被忽略
```go
type User struct {
Name string `gorm:"<-:create"` // 允許讀和創建
Name string `gorm:"<-:update"` // 允許讀和更新
Name string `gorm:"<-"` // 允許讀和寫(創建和更新)
Name string `gorm:"<-:false"` // 允許讀,禁止寫
Name string `gorm:"->"` // 只讀(除非有自定義配置,否則禁止寫)
Name string `gorm:"->;<-:create"` // 允許讀和寫
Name string `gorm:"->:false;<-:create"` // 僅創建(禁止從 db 讀)
Name string `gorm:"-"` // 讀寫操作均會忽略該字段
}
```
### 創建/更新時間追蹤(納秒、毫秒、秒、Time)
GORM 約定使用 `CreatedAt`、`UpdatedAt` 追蹤創建/更新時間。如果您定義了他們,GORM 在創建/更新時會自動填充 [當前時間](../高級主題gorm_config.html#current_time) 至這些字段
要使用不同名稱的字段,您可以配置 `autoCreateTim`、`autoUpdateTim` 標簽
如果您想要保存納秒、毫秒、秒級 UNIX 時間戳,而不是 time,您只需簡單地將 `time.Time` 修改為 `int` 即可
```go
type User struct {
CreatedAt time.Time // 在創建時,如果該字段值為零值,則使用當前時間填充
UpdatedAt int // 在創建時該字段值為零值或者在更新時,使用當前秒級時間戳填充
Updated int64 `gorm:"autoUpdateTime:nano"` // 使用納秒級時間戳填充更新時間
Updated int64 `gorm:"autoUpdateTime:milli"` // 使用毫秒級時間戳填充更新時間
Created int64 `gorm:"autoCreateTime"` // 使用秒級時間戳填充創建時間
}
```
### 嵌入結構體
對于匿名字段,GORM 會將其字段包含在父結構體中,例如:
```go
type User struct {
gorm.Model
Name string
}
// 等效于
type User struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
}
```
對于正常的結構體字段,你也可以通過標簽 `embedded` 將其嵌入,例如:
```go
type Author struct {
Name string
Email string
}
type Blog struct {
ID int
Author Author `gorm:"embedded"`
Upvotes int32
}
// 等效于
type Blog struct {
ID int64
Name string
Email string
Upvotes int32
}
```
并且,您可以使用標簽 `embeddrefix` 來為 db 中的字段名添加前綴,例如:
```go
type Blog struct {
ID int
Author Author `gorm:"embedded;embeddedPrefix:author_"`
Upvotes int32
}
// 等效于
type Blog struct {
ID int64
AuthorName string
AuthorEmail string
Upvotes int32
}
```
### 字段標簽
在聲明模型時,標簽是可選的,GORM 支持以下標簽:
標簽名對大小寫不敏感,但建議使用 `小駝峰(camelCase)` 的命名方式。
| 標簽名 | 說明 |
| :------------- | :----------------------------------------------------------- |
| column | 指定 db 列名 |
| type | 列數據類型,推薦使用兼容性好的通用類型,例如:bool、int、uint、float、string、time、bytes。并可與其他標簽一起使用,例如:`not null`、`size`, `autoIncrement`… 像 `varbinary(8)` 這樣指定數據庫數據類型也是支持的。在使用指定數據庫數據類型時,它需要是完整的數據庫數據類型,如:`MEDIUMINT UNSINED not NULL AUTO_INSTREMENT` |
| size | 指定列大小,例如:`size:256` |
| primaryKey | 指定列為主鍵 |
| unique | 指定列為唯一 |
| default | 指定列的默認值 |
| precision | 指定列的精度 |
| not null | 指定列為 NOT NULL |
| autoIncrement | 指定列為自動增長 |
| embedded | 嵌套字段 |
| embeddedPrefix | 嵌套字段的前綴 |
| autoCreateTime | 創建時追蹤當前時間,對于 `int` 字段,它會追蹤秒級時間戳,您可以使用 `nano`/`milli` 來追蹤納秒、毫秒時間戳,例如:`autoCreateTime:nano` |
| autoUpdateTime | 創建/更新時追蹤當前時間,對于 `int` 字段,它會追蹤秒級時間戳,您可以使用 `nano`/`milli` 來追蹤納秒、毫秒時間戳,例如:`autoUpdateTime:milli` |
| index | 根據參數創建索引,多個字段擁有相同的名稱則創建復合索引,參考 [索引](../高級主題/indexes.html) 獲取詳情 |
| uniqueIndex | 與 `index` 相同,但創建的是唯一索引 |
| check | 創建檢查約束,例如 `check:(age > 13)`,查看 [約束](../高級主題/constraints.html) 獲取詳情 |
| <- | 設置字段寫入的權限, `<-:create` 只創建、`<-:update` 只更新、`<-:false` 無權限 |
| -> | 設置字段讀取權限 |
| - | 忽略此字段(禁止讀寫) |
### 關聯標簽
GORM 允許通過標簽為關聯配置外鍵、約束、many2many 表,詳情請參考 [關聯部分](../關聯/associations.md#tags)