beego使用(3)-模型

模型主要用于和数据库交互,beego提供了ORM,其借鉴了Django和SQLAlchemy的使用方法,在beego中有一些比较特殊的模型定义方法,下面会依次说明。和控制器一样,模型也会有基类,用于一些通用的字段,最常见的是Createtime和Updatetime以及id等。

Base基类

基类中主要定义CreateTime和UpdateTime,其类型是time.Time,而在定义之后还有描述,这个是模型定义特殊的地方,通过这种描述来说明该数据在数据库或者其它情况下的特性。比如这里的CreateTime定义了orm,表示创建时候自动更新,而UpdateTime里面是自动更新,用type指定了mysql里面的类型,并用index设置了索引。而后面的json设置主要是在以json格式返回的时候不返回。而JsonCreateTime则是专门为json返回设置,json中的定义就是返回该属性的属性名,这样设置是因为数据库的CreateTime并不是我们常用的”xxxx-xx-xx”这种类似的格式,而其orm设置为”-“则不再数据库记录。由于需要返回json格式的时间,所以这里需要重定义time.Time的MarshalJSON函数,该函数在将对像转化为json格式的时候调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package models 
import (
"fmt"
"time"
)

type JsonTime time.Time
type Filters map[string]string

func (jt JsonTime) MarshalJSON() ([]byte, error) {
s := time.Time(jt).Format("2006-01-02 15:04:05")
s = fmt.Sprintf(`"%s"`, s)
return []byte(s), nil
}

type Base struct {
CreateTime time.Time `orm:"auto_now_add;type(datetime);index" json:"-"`
UpdateTime time.Time `orm:"auto_now;type(datetime);index" json:"-"`
JsonCreateTime JsonTime `orm:"-" json:"create_time"`
JsonUpdateTime JsonTime `orm:"-" json:"update_time"`
}

func (u *Base) TableEngine() string {
return "INNODB"
}

以上代码中重新定义新的时间类型是因为原本的time.Time的MarshalJSON函数会转化成UTC的时间格式,所以想要得到想要的只能重定义。

实例类型

下面给出了一个讲解的例子,定义了一个用户类。其中包括Id和Name,id是自增长的,name是一个128字节的字符串类型,这里使用了form,这个设置用于可以和数据库绑定操作。

1
2
3
4
5
type TestUser struct {
Id int64 `orm:"auto" json:"id" form:"-"`
Name string `orm:"size(128);index" json:"name" form:"name"`
Base
}

由于这里继承了Base,所以需要重写MarshalJSON函数,用于转换时间,这里可以看到用了type,这主要是因为json.Marshal对某个对象进行json格式化的时候,会调用该对象的MarshalJSON函数,这样会调用死循环。所以用type重命名后只继承了属性,没有继承方法。

1
2
3
4
5
6
func (tu TestUser) MarshalJSON() ([]byte, error) { 
tu.JsonCreateTime, tu.JsonUpdateTime = JsonTime(ct.CreateTime), JsonTime(ct.UpdateTime)
type Alias TestUser
t, err := json.Marshal(Alias(tu))
return t, err
}

下面是对数据库的设置,通过该函数可以设置某个或几个属性的唯一性。比如这里将Name设置成了唯一。

1
2
3
4
5
func (tu *TestUser) TableUnique() [][]string {
return [][]string{
[]string{"Name"},
}
}

在具体model中init函数中要注册对应的对象,这里就是让TestUser注册到orm中。

1
2
3
func init() {
orm.RegisterModel(new(TestUser))
}

而对于model的具体实现则是定义TestUser的成员函数。比如下面这种:

1
2
func AddUser(u *TestUser) (u *TestUser, err error) {
}

ORM的基本操作

1、添加

1
2
o := orm.NewOrm()
id, err := o.Insert(obj)

2、删除

1
2
3
o := orm.NewOrm()
_, err := o.Delete(obj)
_, err = o.QueryTable(new(TestUser)).Filter("id", id).Delete()

3、更新

1
2
3
o := orm.NewOrm()
obj.prop=value
_, err := o.Update(obj)

4、查询

1
2
3
o := orm.NewOrm()
u := new(TestUser)
_, err := o.QueryTable("test_user").Filter("id", id).Filter("name", name).One(u)

在beego中定义的struc使用orm在数据库生成的时候会将大写字母变成小写,并以大写字母为标志用下划线分隔。比如AaaBbb会编程aaa_bbb这种,表名也是如此。除了使用表名也可以直接使用对象查询,比如下面这样:

1
_, err := o.QueryTable(new(TestUser)).Filter("id", id).Filter("name", name).One(u)

对于多个对象的查询

1
2
3
us := []TestUser{}
o := orm.NewOrm()
_, err = o.QueryTable("test_user").Filter("name",name).All(&us)

分页查询

1
2
3
4
qs := o.QueryTable("test_user")
total, err = qs.Count()
offset := (page - 1) * pagesizeqs
_, err = qs.Limit(pagesize, offset).All(&us)

排序

1
qs.OrderBy([]string{"-id"}...)

数组类型查询

1
qs.Filter("id__in", Ids...)

模糊查询

1
qs.Filter("name__startswith", v)

判断是否有结果

1
qs.Exist()

5、事务
由于有些情况下面需要进行多次数据库操作,所以需要用到事务处理。

1
2
3
4
5
6
7
8
9
10
11
o := orm.NewOrm()
defer func() {
if r := recover(); r != nil {
err = errors.New(fmt.Sprintf("panic|%v", r))
}
if err != nil {
o.Rollback()
}
}()
o.Begin
o.Commit()

数据库注册

数据库注册是需要写在程序开始的时候,所以是在main.go的init函数中。

1
2
dbUri := beego.AppConfig.String("database")
orm.RegisterDataBase("default", "mysql", dbUri, 31, 800)

其中配置文件中有:

1
database = root@tcp(127.0.0.1:3306)/testdb?charset=utf8&loc=Asia%2FShanghai

如果需要重新创建数据表

1
orm.RunSyncdb("default", false, false)

第三个参数true表示表存在则给出提示。