diff --git a/orm/docs/zh/CustomFields.md b/orm/docs/zh/CustomFields.md new file mode 100644 index 00000000..3a3c835c --- /dev/null +++ b/orm/docs/zh/CustomFields.md @@ -0,0 +1,38 @@ +## Custom Fields + + TypeBooleanField = 1 << iota + + // string + TypeCharField + + // string + TypeTextField + + // time.Time + TypeDateField + // time.Time + TypeDateTimeField + + // int16 + TypeSmallIntegerField + // int32 + TypeIntegerField + // int64 + TypeBigIntegerField + // uint16 + TypePositiveSmallIntegerField + // uint32 + TypePositiveIntegerField + // uint64 + TypePositiveBigIntegerField + + // float64 + TypeFloatField + // float64 + TypeDecimalField + + RelForeignKey + RelOneToOne + RelManyToMany + RelReverseOne + RelReverseMany \ No newline at end of file diff --git a/orm/docs/zh/Models.md b/orm/docs/zh/Models.md new file mode 100644 index 00000000..284f40d2 --- /dev/null +++ b/orm/docs/zh/Models.md @@ -0,0 +1,291 @@ +## Model Definition + +比较全面的 Model 定义例子,后文所有的例子如无特殊说明都以这个为基础。 + +当前还没有完成自动创建表的功能,所以提供一个 [Models.sql](Models.sql) 测试 + +note: 根据文档的更新,随时都可能更新这个 Model + +##### models.go: + + package main + + import ( + "github.com/astaxie/beego/orm" + "time" + ) + + type User struct { + Id int `orm:"auto"` // 设置为auto主键 + UserName string `orm:"size(30);unique"` // 设置字段为unique + Email string `orm:"size(100)"` // 设置string字段长度时,会使用varchar类型 + Password string `orm:"size(100)"` + Status int16 `orm:"choices(0,1,2,3);defalut(0)"` // choices设置可选值 + IsStaff bool `orm:"default(false)"` // default设置默认值 + IsActive bool `orm:"default(0)"` + Created time.Time `orm:"auto_now_add;type(date)"` // 创建时自动设置时间 + Updated time.Time `orm:"auto_now"` // 每次更新时自动设置时间 + Profile *Profile `orm:"null;rel(one);on_delete(set_null)"` // OneToOne relation, 级联删除时设置为NULL + Posts []*Post `orm:"reverse(many)" json:"-"` // fk 的反向关系 + orm.Manager `json:"-"` // 每个model都需要定义orm.Manager + } + + // 定义NewModel进行orm.Manager的初始化(必须) + func NewUser() *User { + obj := new(User) + obj.Manager.Init(obj) + return obj + } + + type Profile struct { + Id int `orm:"auto"` + Age int16 `` + Money float64 `` + User *User `orm:"reverse(one)" json:"-"` // 设置反向关系(字段可选) + orm.Manager `json:"-"` + } + + func (u *Profile) TableName() string { + return "profile" // 自定义表名 + } + + func NewProfile() *Profile { + obj := new(Profile) + obj.Manager.Init(obj) + return obj + } + + type Post struct { + Id int `orm:"auto"` + User *User `orm:"rel(fk)"` // RelForeignKey relation + Title string `orm:"size(60)"` + Content string `` + Created time.Time `` + Updated time.Time `` + Tags []*Tag `orm:"rel(m2m)"` // ManyToMany relation + orm.Manager `json:"-"` + } + + func NewPost() *Post { + obj := new(Post) + obj.Manager.Init(obj) + return obj + } + + type Tag struct { + Id int `orm:"auto"` + Name string `orm:"size(30)"` + Status int16 `orm:"choices(0,1,2);default(0)"` + Posts []*Post `orm:"reverse(many)" json:"-"` + orm.Manager `json:"-"` + } + + func NewTag() *Tag { + obj := new(Tag) + obj.Manager.Init(obj) + return obj + } + + type Comment struct { + Id int `orm:"auto"` + Post *Post `orm:"rel(fk)"` + Content string `` + Parent *Comment `orm:"null;rel(fk)"` // null设置allow NULL + Status int16 `orm:"choices(0,1,2);default(0)"` + Created time.Time `orm:"auto_now_add"` + orm.Manager `json:"-"` + } + + func NewComment() *Comment { + obj := new(Comment) + obj.Manager.Init(obj) + return obj + } + + func init() { + // 需要在init中注册定义的model + orm.RegisterModel(new(User)) + orm.RegisterModel(new(Profile)) + orm.RegisterModel(new(Post)) + orm.RegisterModel(new(Tag)) + orm.RegisterModel(new(Comment)) + } + +## Field Type + +现在 orm 支持下面的字段形式 + +| go type | field type | mysql type +| :--- | :--- | :--- +| bool | TypeBooleanField | tinyint +| string | TypeCharField | varchar +| string | TypeTextField | longtext +| time.Time | TypeDateField | date +| time.TIme | TypeDateTimeField | datetime +| int16 |TypeSmallIntegerField | int(4) +| int, int32 |TypeIntegerField | int(11) +| int64 |TypeBigIntegerField | bigint(20) +| uint, uint16 |TypePositiveSmallIntegerField | int(4) unsigned +| uint32 |TypePositiveIntegerField | int(11) unsigned +| uint64 |TypePositiveBigIntegerField | bigint(20) unsigned +| float32, float64 | TypeFloatField | double +| float32, float64 | TypeDecimalField | double(digits, decimals) + +关系型的字段,其字段类型取决于对应的主键。 + +* RelForeignKey +* RelOneToOne +* RelManyToMany +* RelReverseOne +* RelReverseMany + +## Field Options + + `orm:"null;rel(fk)"` + +通常每个 Field 的 StructTag 里包含两种类型的设置,类似 null 的 bool 型设置,还有 类似 rel(fk) 的指定值设置,bool 型默认为 false,指定以后即表示为 true + +多个设置间使用 `;` 分隔,设置的值如果是多个,使用 `,` 分隔。 + +#### auto + +设置为 Autoincrement Primary Key + +#### pk + +设置为 Primary Key + +#### null + +数据库表默认为 `NOT NULL`,设置 null 代表 `ALLOW NULL` + +#### blank + +设置 string 类型的字段允许为空,否则 clean 会返回错误 + +#### index + +为字段增加索引 + +#### unique + +为字段增加 unique 键 + +#### column + +为字段设置 db 字段的名称 + + UserName `orm:"column(db_user_name)"` + +#### default + +为字段设置默认值,类型必须符合 + + Status int `orm:"default(1)"` + +#### choices + +为字段设置一组可选的值,类型必须符合。其他值 clean 会返回错误 + + Status int `orm:"choices(1,2,3,4)"` + +#### size (string) + +string 类型字段设置 size 以后,db type 将使用 varchar + + Title string `orm:"size(60)"` + +#### digits / decimals + +设置 float32, float64 类型的浮点精度 + + Money float64 `orm:"digits(12);decimals(4)"` + +总长度 12 小数点后 4 位 eg: `99999999.9999` + +#### auto_now / auto_now_add + + Created time.Time `auto_now_add` + Updated time.Time `auto_now` + +* auto_now 每次 model 保存时都会对时间自动更新 +* auto_now_add 第一次保存时才设置时间 + +对于批量的 update 此设置是不生效的 + +#### type + +设置为 date, time.Time 字段的对应 db 类型使用 date + + Created time.Time `orm:"auto_now_add;type(date)"` + +## Relation Field Options + +#### rel / reverse + +RelOneToOne: + + type User struct { + ... + Profile *Profile `orm:"null;rel(one);on_delete(set_null)"` + +对应的反向关系 RelReverseOne: + + type Profile struct { + ... + User *User `orm:"reverse(one)" json:"-"` + +RelForeignKey: + + type Post struct { + ... + User*User `orm:"rel(fk)"` // RelForeignKey relation + +对应的反向关系 RelReverseMany: + + type User struct { + ... + Posts []*Post `orm:"reverse(many)" json:"-"` // fk 的反向关系 + +RelManyToMany: + + type Post struct { + ... + Tags []*Tag `orm:"rel(m2m)"` // ManyToMany relation + +对应的反向关系 RelReverseMany: + + type Tag struct { + ... + Posts []*Post `orm:"reverse(many)" json:"-"` + +#### rel_table / rel_through + +此设置针对 `orm:"rel(m2m)"` 的关系字段 + + rel_table 设置自动生成的 m2m 关系表的名称 + rel_through 如果要在 m2m 关系中使用自定义的 m2m 关系表 + 通过这个设置其名称,格式为 pkg.path.ModelName + eg: app.models.PostTagRel + PostTagRel 表需要有到 Post 和 Tag 的关系 + +当设置 rel_table 时会忽略 rel_through + +#### on_delete + +设置对应的 rel 关系删除时,如何处理关系字段。 + + cascade 级联删除(默认值) + set_null 设置为 NULL,需要设置 null = true + set_default 设置为默认值,需要设置 default 值 + do_nothing 什么也不做,忽略 + + type User struct { + ... + Profile *Profile `orm:"null;rel(one);on_delete(set_null)"` + ... + type Profile struct { + ... + User *User `orm:"reverse(one)" json:"-"` + + 删除 Profile 时将设置 User.Profile 的数据库字段为 NULL \ No newline at end of file diff --git a/orm/docs/zh/Models.sql b/orm/docs/zh/Models.sql new file mode 100644 index 00000000..2d9d5ce0 --- /dev/null +++ b/orm/docs/zh/Models.sql @@ -0,0 +1,83 @@ +SET NAMES utf8; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for `comment` +-- ---------------------------- +DROP TABLE IF EXISTS `comment`; +CREATE TABLE `comment` ( + `id` int(11) NOT NULL, + `post_id` bigint(200) NOT NULL, + `content` longtext NOT NULL, + `parent_id` int(11) DEFAULT NULL, + `status` smallint(4) NOT NULL, + `created` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for `post` +-- ---------------------------- +DROP TABLE IF EXISTS `post`; +CREATE TABLE `post` ( + `id` int(11) NOT NULL, + `user_id` int(11) NOT NULL, + `title` varchar(60) NOT NULL, + `content` longtext NOT NULL, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for `post_tag_rel` +-- ---------------------------- +DROP TABLE IF EXISTS `post_tag_rel`; +CREATE TABLE `post_tag_rel` ( + `id` int(11) NOT NULL, + `post_id` int(11) NOT NULL, + `tag_id` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for `tag` +-- ---------------------------- +DROP TABLE IF EXISTS `tag`; +CREATE TABLE `tag` ( + `id` int(11) NOT NULL, + `name` varchar(30) NOT NULL, + `status` smallint(4) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for `user` +-- ---------------------------- +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_name` varchar(30) NOT NULL, + `email` varchar(100) NOT NULL, + `password` varchar(30) NOT NULL, + `status` smallint(4) NOT NULL, + `is_staff` tinyint(1) NOT NULL, + `is_active` tinyint(1) NOT NULL, + `created` date NOT NULL, + `updated` datetime NOT NULL, + `profile_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for `profile` +-- ---------------------------- +DROP TABLE IF EXISTS `profile`; +CREATE TABLE `profile` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `age` smallint(4) NOT NULL, + `money` double NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/orm/docs/zh/Object.md b/orm/docs/zh/Object.md new file mode 100644 index 00000000..3d0ce14d --- /dev/null +++ b/orm/docs/zh/Object.md @@ -0,0 +1,59 @@ +## Object + +对 object 操作的三个方法 Insert / Update / Delete + + o := orm.NewOrm() + user := NewUser() + user.UserName = "slene" + user.Password = "password" + user.Email = "vslene@gmail.com" + obj := o.Object(user) + fmt.Println(obj.Insert()) + user.UserName = "Your" + fmt.Println(obj.Update()) + fmt.Println(obj.Delete()) + +### Read + + var user User + err := o.QueryTable("user").Filter("id", 1).One(&user) + if err != orm.ErrMultiRows { + fmt.Println(user.UserName) + } + +### Create + + profile := NewProfile() + profile.Age = 30 + profile.Money = 9.8 + + user := NewUser() + user.Profile = profile + user.UserName = "slene" + user.Password = "password" + user.Email = "vslene@gmail.com" + user.IsActive = true + + fmt.Println(o.Object(profile).Insert()) + fmt.Println(o.Object(user).Insert()) + fmt.Println(user.Id) + +创建后会自动对 auto 的 field 赋值 + +### Update + + var user User + err := o.QueryTable("user").Filter("id", 1).One(&user) + if err != orm.ErrMultiRows { + fmt.Println(user.UserName) + } + user.UserName = "MyName" + o.Object(&user).Update() + +### Delete + + o.Object(user).Delete() + +Delete 操作会对反向关系进行操作,此例中 Post 拥有一个到 User 的外键。删除 User 的时候。如果 on_delete 设置为默认的级联操作,将删除对应的 Post + +删除以后会清除 auto field 的值 diff --git a/orm/docs/zh/Orm.md b/orm/docs/zh/Orm.md new file mode 100644 index 00000000..f96e3966 --- /dev/null +++ b/orm/docs/zh/Orm.md @@ -0,0 +1,64 @@ +## Orm + +beego/orm 的使用方法 + + package main + + import ( + "fmt" + "github.com/astaxie/beego/orm" + _ "github.com/go-sql-driver/mysql" + ) + + func init() { + // 这个用来设置 driverName 对应的数据库类型 + // mysql / sqlite3 / postgres 这三种是默认已经注册过的,所以可以无需设置 + orm.RegisterDriver("mysql", orm.DR_MySQL) + + // 参数1 自定义的数据库名称,用来在orm中切换数据库使用 + // 参数2 driverName + // 参数3 对应的链接字符串 + // 参数4 设置最大的空闲连接数,使用 golang 自己的连接池 + orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8", 30) + } + + func main() { + orm.BootStrap() // 强制在 main 函数里调用,检查 Model 关系,检测数据库参数,调用 orm 提供的 Command + + o := orm.NewOrm() + o.Using("default") // 默认使用 default,你可以指定为其他数据库 + + profile := NewProfile() + profile.Age = 30 + profile.Money = 9.8 + + user := NewUser() + user.Profile = profile + user.UserName = "slene" + user.Password = "password" + user.Email = "vslene@gmail.com" + user.IsActive = true + + fmt.Println(o.Object(profile).Insert()) + fmt.Println(o.Object(user).Insert()) + + var params []orm.Params + if cnt, err := o.QueryTable("user").RelatedSel().Limit(3).OrderBy("-id").Values(¶ms); err != nil { + fmt.Println(err) + } else { + fmt.Println(cnt) + for _, p := range params { + fmt.Println(p) + } + } + + var users []*User + if cnt, err := o.QueryTable("user").RelatedSel().Limit(3).OrderBy("-id").All(&users); err != nil { + fmt.Println(err) + } else { + fmt.Println(cnt) + for _, u := range users { + fmt.Println(u.Id, u.Profile) + } + } + } diff --git a/orm/docs/zh/Query.md b/orm/docs/zh/Query.md new file mode 100644 index 00000000..91786743 --- /dev/null +++ b/orm/docs/zh/Query.md @@ -0,0 +1,204 @@ +## Query + +orm 以 **QuerySeter** 来组织查询,每个返回 **QuerySeter** 的方法都会获得一个新的 **QuerySeter** 对象。 + +基本使用方法: + + o := orm.NewOrm() + + // 获取 QuerySeter 对象,user 为表名 + qs := o.QueryTable("user") + + // 也可以直接使用对象作为表名 + user := NewUser() + qs = o.QueryTable(user) // 返回 QuerySeter + +### expr + +QuerySeter 中用于描述字段和 sql 操作符使用简单的 expr 查询方法 + +字段组合的前后顺序依照表的关系,比如 User 表拥有 Profile 的外键,那么对 User 表查询对应的 Profile.Age 为条件,则使用 `Profile__Age` 注意,字段的分隔符号使用双下划线 `__`,除了描述字段, expr 的尾部可以增加操作符以执行对应的 sql 操作。比如 `Profile__Age__gt` 代表 Profile.Age > 18 的条件查询。 + +注释后面将描述对应的 sql 语句,仅仅是描述 expr 的类似结果,并不代表实际生成的语句。 + + qs.Filter("id", 1) // WHERE id = 1 + qs.Filter("profile__age", 18) // WHERE profile.age = 18 + qs.Filter("Profile__Age", 18) // 使用字段名和Field名都是允许的 + qs.Filter("profile__age", 18) // WHERE profile.age = 18 + qs.Filter("profile__age__gt", 18) // WHERE profile.age > 18 + qs.Filter("profile__age__gte", 18) // WHERE profile.age >= 18 + qs.Filter("profile__age__in", 18, 20) // WHERE profile.age IN (18, 20) + + qs.Filter("profile__age__in", 18, 20).Exclude("profile__money__lt", 1000) + // WHERE profile.age IN (18, 20) AND NOT profile.money < 1000 + +### Operators + +当前支持的操作符号 + +#### exact + +Filter / Exclude / Condition expr 的默认值 + + qs.Filter("user_name", "slene") // WHERE user_name = 'slene' + qs.Filter("user_name__exact", "slene") // WHERE user_name = 'slene' + qs.Filter("profile", nil) // WHERE profile_id IS NULL + +#### iexact + + qs.Filter("user_name__iexact", "slene") + // WHERE user_name LIKE 'slene' + // 大小写不敏感,匹配任意 'Slene' 'sLENE' + +#### contains + + qs.Filter("user_name__contains", "slene") + // WHERE user_name LIKE BINARY '%slene%' + // 大小写敏感, 匹配包含 slene 的字符 + +#### icontains + + qs.Filter("user_name__icontains", "slene") + // WHERE user_name LIKE '%slene%' + // 大小写不敏感, 匹配任意 'im Slene', 'im sLENE' + +#### in + + qs.Filter("profile__age__in", 17, 18, 19, 20) + // WHERE profile.age IN (17, 18, 19, 20) + +#### gt / gte + + qs.Filter("profile__age__gt", 17) + // WHERE profile.age > 17 + + qs.Filter("profile__age__gte", 18) + // WHERE profile.age >= 18 + +#### lt / lte + + qs.Filter("profile__age__lt", 17) + // WHERE profile.age < 17 + + qs.Filter("profile__age__lte", 18) + // WHERE profile.age <= 18 + +#### startswith + + qs.Filter("user_name__startswith", "slene") + // WHERE user_name LIKE BINARY 'slene%' + // 大小写敏感, 匹配以 'slene' 起始的字符串 + +#### istartswith + + qs.Filter("user_name__istartswith", "slene") + // WHERE user_name LIKE 'slene%' + // 大小写不敏感, 匹配任意以 'slene', 'Slene' 起始的字符串 + + +#### endswith + + qs.Filter("user_name__endswith", "slene") + // WHERE user_name LIKE BINARY '%slene' + // 大小写敏感, 匹配以 'slene' 结束的字符串 + +#### iendswith + + qs.Filter("user_name__startswith", "slene") + // WHERE user_name LIKE '%slene' + // 大小写不敏感, 匹配任意以 'slene', 'Slene' 结束的字符串 + +#### isnull + + qs.Filter("profile__isnull", true) + qs.Filter("profile_id__isnull", true) + // WHERE profile_id IS NULL + + qs.Filter("profile__isnull", false) + // WHERE profile_id IS NOT NULL + +## QuerySeter + +#### Filter + +多个 Filter 之间使用 `AND` 连接 + + qs.Filter("profile__isnull", true).Filter("user_name", "slene") + // WHERE profile_id IS NULL AND user_name = 'slene' + +#### Exclude + +使用 `NOT` 排除条件 + +多个 Exclude 之间使用 `AND` 连接 + + qs.Exclude("profile__isnull", true).Filter("user_name", "slene") + // WHERE NOT profile_id IS NULL AND user_name = 'slene' + +#### Limit + +限制最大返回数据行数,第二个参数可以设置 `Offset` + + var DefaultRowsLimit = 1000 // orm 默认的 limit 值为 1000 + + // 默认情况下 select 查询的最大行数为 1000 + // LIMIT 1000 + + qs.Limit(10) + // LIMIT 10 + + qs.Limit(10, 20) + // LIMIT 10 OFFSET 20 + + qs.Limit(-1) + // no limit + + qs.Limit(-1, 100) + // LIMIT 18446744073709551615 OFFSET 100 + // 18446744073709551615 是 1<<64 - 1 用来指定无 limit 限制 但有 offset 偏移的情况 + +#### Offset + +设置 偏移行数 + + qs.OFFSET(20) + // LIMIT 1000 OFFSET 20 + +#### OrderBy + +参数使用 **expr** + +在 expr 前使用减号 `-` 表示 `DESC` 的排列 + + qs.OrderBy("id", "-profile__age") + // ORDER BY id ASC, profile.age DESC + + qs.OrderBy("-profile__money", "profile") + // ORDER BY profile.money DESC, profile_id ASC + +#### RelatedSel + +关系查询,参数使用 **expr** + + var DefaultRelsDepth = 5 // 默认情况下直接调用 RelatedSel 将进行最大 5 层的关系查询 + + qs := o.QueryTable("post") + + qs.RelateSel() + // INNER JOIN user ... LEFT OUTER JOIN profile ... + + qs.RelateSel("user") + // INNER JOIN user ... + // 设置 expr 只对设置的字段进行关系查询 + + // 对设置 null 属性的 Field 将使用 LEFT OUTER JOIN + + + + + + + + + + diff --git a/orm/docs/zh/README.md b/orm/docs/zh/README.md index e69de29b..789d6504 100644 --- a/orm/docs/zh/README.md +++ b/orm/docs/zh/README.md @@ -0,0 +1,15 @@ +## beego orm 介绍 + + +## 指南 + +1. [Model Definition](Models.md) +2. Custom Fields +3. [Orm](Orm.md) +4. [Object](Object.md) +5. [Query](Query.md) +6. Condition +7. Raw +8. Transaction +9. Faq +