网易云课堂中知了海龙老师的Go/Golang/Beego微服务基础实战
学习课件笔记,便于后续自己复习总结。
环境配置: GOPATH配置
在系统变量中新建,变量名:GOPATH变量值:E:\go\workspace 你的工作路径变量名:GOROOT变量值:E:\go\install go的安装路径 变量名:Path 增加值:%GOROOT%\bin;%GOPATH%\bin; 存放可执行文件检查配置是否成功 go env 查看GOPATH和GOROOT是否正确 1.1.2 git安装管理代码
1.1.3 工程管理工作目录下新建三个文件夹
src:用于以代码包的形式组织并保存go源码文件,pkg:用于存放经由go install命令构建的安装后的代码包,不需要手动创建bin:与pkg目录类似,在通过go install命令完成安装后,保存由go命令源码间生成的可执行文件 1.1.4 bee工具:管理beego项目
安装bee工具
go get github.com/beego/bee注意 将$GOPATH/bin添加到环境变量,这go get的时候会在bin目录下生成bee的可执行文件使用bee工具 运行bee可以看到bee参数,比如new,api,run等 1.1.5 安装beego:go get github.com/astaxie/beego 升级 go get -u github.com/astaxie/beego
源码下载升级 访问 https://github.com/astaxie/beego ,下载源码,然后覆盖到 $GOPATH/src/github.com/astaxie/beego 目录,然后通过本地执行安装就可以升级了:go install github
1.1.6 安装goland编辑工具 1.2 第一个beego项目bee new beego_project(项目名) BeegoProject 注意命名规范
create E:\go\workspace\src\beego_project
create E:\go\workspace\src\beego_project\conf\
create E:\go\workspace\src\beego_project\controllers\
create E:\go\workspace\src\beego_project\models\
create E:\go\workspace\src\beego_project\routers\
create E:\go\workspace\src\beego_project\tests\
create E:\go\workspace\src\beego_project\static\
create E:\go\workspace\src\beego_project\static\js\
create E:\go\workspace\src\beego_project\static\css\
create E:\go\workspace\src\beego_project\static\img\
create E:\go\workspace\src\beego_project\views\
create E:\go\workspace\src\beego_project\conf\app.conf
create E:\go\workspace\src\beego_project\controllers\default.go
create E:\go\workspace\src\beego_project\views\index.tpl
create E:\go\workspace\src\beego_project\routers\router.go
create E:\go\workspace\src\beego_project\tests\default_test.go
create E:\go\workspace\src\beego_project\main.go
New application successfully created! --创建成功标志
一个典型的 MVC 架构的应用,main.go是入口文件。
cd到创建好的项目:cd beego_project
然后运行启动命令:bee run
启动成功标志
访问:http:127.0.0.1:8080
也可以通过:
go run main.go
启动,不通过bee run
go env -w GO111MODULE=on // 111指go的1.11版本
1.3.2 go-mod的使用
进入项目路径:
go mod init 【项目名】
go mod vendor
1.4 运行原理
运行项目的两种方式
进入项目中:
bee run // 可以检查代码是否修改,重新启动
go run main.go
二、前后端数据交互
2.1 模板文件及静态文件的使用
2.1.1 模板文件
默认路径:项目根目录下的views文件夹
修改路径:
beego.Run()之前设置:beego.SetViewsPath(“testview”)配置文件中设置:viewspath = “myview” 2.1.2 静态文件默认路径:项目根目录下的static文件夹
修改路径:beego.Run()之前设置:beego.SetStaticPath("/static",“front”)
注意:第一个参数是前端使用的开始路径,第二个参数是对应的文件夹
2.2 各种类型数据渲染 2.2.1 字符串渲染u.Ctx.WriteString(“hello world”)
2.2.2 模板渲染
结构体数据渲染
结构体:
type Student struct{
Name string
Age int
Gender string
}
赋值:
c.Data["student"] = Student{Name:"知了课堂",Age:18,Gender:"男"}
前端使用:
学生姓名:{{.student.Name}}
学生年龄:{{.student.Age}}
学生性别:{{.student.Gender}}
注意:结构体中的字段要在其他地方使用,比如首字母大写
数组数据渲染
lista := [5]int{1,2,3,4,5}
c.Data["arr"] = lista
前端:
第一种:
{{range $i,$v := .arr}}
{{$i}}
{{$v}}
{{end}
第二种:
{{range .arr}}
{{.}}
{{end}}
结构体数组渲染
结构体:
type student struct {
Name string
Age int
Gender string
}
赋值:
arr_struct := [3]student{{Name:"hl",Age:18,Gender:"男"},{Name:"hallen",Age:19,Gender:"男"},{Name:"hallen1",Age:191,Gender:"男"}}
c.Data["arr_struct"] = arr_struct
前端获取:先循环数组,在获取结构体变量,注意是大写
{{range $v := .arr_struct}}
{{$v.Name}}
{{$v.Age}}
{{$v.Gender}}
{{range .books}}
{{.Name}}
{{.Author}}
{{end}}
map数据渲染
//teacher :=map[string]string{"name":"张三","age":"18"}
teacher :=make(map[string]string)
teacher["name"]="老王"
teacher["age"]="30"
c.Data["teacher"] = teacher
前端:
取出key对应的值
{{.teacher.name}}
{{.teacher.age}}
取出所有的key和value:
{{range $k,$v := .teacher}}
{{$k}}
{{$v}}
{{end}}
结构体和map组合渲染
结构体:
type student struct {
Name string
Age int
Gender string
}
赋值:
mapa := make(map[int]student)
mapa[101] = student{Name:"张三1",Age:181,Gender:"男"}
mapa[102] = student{Name:"张三2",Age:182,Gender:"男"}
mapa[103] = student{Name:"张三3",Age:183,Gender:"男"}
c.Data["hero_map"] = mapa
前端获取:先循环map,在获取结构体变量,注意是大写
{{range $v :=.hero_map}}
{{$v.Name}}
{{end}}
切片数据渲染
listb := []int{1,2,3,4,5,6,7}
c.Data["list_b"] = listb
前端:只有一个值的时候默认是切片的元素,而不是角标
{{range $v := .list_b}}
{{$v}}
{{end}}
2.3 获取请求数据
获取url上的参数,?后面的 :http://127.0.0.1:8090/user/?id=111
可以使用数据绑定,请看数据绑定章节
GetString获取数据:
路由: beego.Router("/user", &controllers_user.UserController{})
访问路径:http://127.0.0.1:8090/user/?id=111
获取数据:
id := c.Input().Get("id")
id2 := c.GetString("id")
这种方式不行:
id3 := c.Ctx.Input.Param(":id")
获取url上的参数,/:id的 :http://127.0.0.1:8090/user/111
路由:beego.Router("/user/?:id:int", &controllers_user.UserController{})
访问路径:http://127.0.0.1:8090/user/111
获取数据:
id := c.GetString(":id")
id2 := c.Ctx.Input.Param(":id")
这种方式不行:
id3 := c.Input().Get(":id")
获取请求信息:
this.Ctx.Request 所有的请求信息
this.Ctx.Request.Header 请求头
this.Ctx.Request.Host 请求的主机
this.Ctx.Request.Method 请求的方法
获取form表单数据:
GetString(key string) string
GetStrings(key string) []string
GetInt(key string) (int64, error) --返回两个值
GetBool(key string) (bool, error) --返回两个值
GetFloat(key string) (float64, error) --返回两个值
举例:
前端form表单:
<form action="/user" method="post">
年龄1:<input type="text" name="age"><br>
姓名1:<input type="text" name="name"><br>
地址:<input type="text" name="addr"><br>
姓名2:<input type="text" name="name"><br>
年龄2:<input type="text" name="age"><br>
是:<input type="radio" name="is_true" value="true">
否:<input type="radio" name="is_true" value="false"><br>
价格:<input type="text" name="price"><br>
<input type="submit" value="提交"><br>
form>
获取数据:
name := c.Input().Get("name") 获取的是第一个name的值
names := c.GetStrings("name") 获取所有的name的值,是个数组
age := c.Input().Get("age")
age,_ := c.GetInt64("age")
is_true , _ := c.GetBool("is_true")
price , _ := c.GetFloat("price")
form表单解析到结构体
type Student struct {
Id int
Name string `form:"user_name"`
Password string `form:"password"`
}
func (s *StudentController) Post() {
student := Student{} // 这里注意,变量名不能和结构体对象名不能相同
if err := s.ParseForm(&student); err != nil {
//handle error
return
}
fmt.Println(student)
fmt.Println(student.Name)
fmt.Println(student.Age)
fmt.Println(student.Addr)
s.TplName = "index.tpl"
}
ajax提交
<script src="/static/js/jquery.min.js"></script>
<script>
var add_student = document.getElementById("add_student");
add_student.onclick = function (ev) {
var user_name = document.getElementById("user_name").value;
var password = document.getElementById("password").value;
$.ajax({
url:"/add_user_ajax",
type:"POST",
data:{
"user_name":user_name,
"password":password
},
success:function (data) {
if(data["code"] == 200){
alert(data["msg"])
}else {
alert("添加失败")
}
},
fail:function (data) {
}
})
}
</script>
2.4 文件上传
2.4.1 form方式上传
前端
前端:
<form action="/uploadFile" method="post" enctype="multipart/form-data">
上传文件:<input type="file" name="upload_file">
<input type="submit" value="提交">
form>
注意:
这里必须设置enctype="multipart/form-data",不然浏览器不会传送文件
后端
type Upload struct {
beego.Controller
}
func (u *Upload)Get() {
u.TplName = "upload_file.html"
}
func (u *Upload)Post() {
// 获取上传的文件
f,h,err := u.GetFile("upload_file")
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
// 生成时间戳,防止重名
timeUnix:=time.Now().Unix() // int64类型
time_str := strconv.FormatInt(timeUnix,10) // 将int64转为字符串 convert:转换
// 保存获取到的文件
err1 := u.SaveToFile("upload_file","static/upload/"+time_str+h.Filename) // 这里必须有这个路径才会保存成功, 否则报错:The system cannot find the path specified
if err1 != nil{
fmt.Println(err1)
}
u.TplName = "index.tpl"
}
注意:
相同文件名的文件会被后面上传的覆盖,可以使用时间戳重命名文件
二、ajax方式上传
前端
<script src="/static/js/jquery.min.js"></script>
<form>
<input type="file" name="uploadfile" id="uploadfile">
<input type="button" value="提交" id="btn">
</form>
<script>
var btn = document.getElementById("btn");
btn.onclick = function (ev) {
var data = new FormData();
data.append("uploadfile", $("#uploadfile")[0].files[0]);
$.ajax({
url: "/upload",
type: "POST",
data: data,
contentType: false,
processData: false,
success:function (data) {
}
})
}
</script>
processData 默认为true,当设置为true的时候,jquery ajax 提交的时候不会序列化 data,而是直接使用datacontentType: false 不使用默认的application/x-www-form-urlencoded这种contentType分界符:目的是防止上传文件中出现分界符导致服务器无法正确识别文件起始位置ajax 中 contentType 设置为 false 是为了避免 JQuery 对其 *** 作,从而失去分界符
2.5 多种格式数据输出
背景:假如前端或者提供的接口必须以指定的格式传输
json格式:会设置 content-type 为 application/json
type TestController struct {
beego.Controller
}
type Person struct {
Id int
Name string
Gender string
}
func (g *TestController)Get() {
test_data := Person{Id:123,Name:"zhiliao",Gender:"男"}
g.Data["json"] = &test_data -----这里必须叫json,因为ServeJSON()解析json变量的
fmt.Println(test_data)
g.ServeJSON()
//g.TplName = "test.html"
}
xml格式:会设置 content-type 为 application/xml
test_data := Person{Id:123,Name:"zhiliao",Gender:"男"}
g.Data["xml"] = &test_data -----这里必须叫xml,同上
g.ServeXML()
jsonp格式:会设置 content-type 为 application/javascript
test_data := Person{Id:123,Name:"zhiliao",Gender:"男"}
g.Data["jsonp"] = &test_data -----这里必须叫jsonp,同上
g.ServeJSONP()
yaml格式:会以文件传输的方式,yaml就是键值对
test_data := Person{Id:123,Name:"zhiliao",Gender:"男"}
g.Data["yaml"] = &test_data
g.ServeYAML()
yaml格式:
student:
- name : "zhiliao"
- age : 18
teacher:
- name : "laowang"
-age : 38
addr: "市区"
说明:
大小写敏感使用缩进表示层级关系缩进不允许使用tab,只允许空格缩进的空格数不重要,只要相同层级的元素左对齐即可'#'表示注释以 - 开头的行表示构成一个数组,支持多维数组 三、view模块 3.1 常用模板语法 3.1.1 基本语法使用.
来访问当前位置的上下文
使用$
来引用当前模板根级的上下文
使用 $.
引用模板中的根级上下文
支持go语言的符号,这里只是符号的支持
① 字符串:{ { “zhiliao ” } }
② 原始字符串:{ { `zhiliao` } } 不会转义
③ 字节类型:{ { ’ a’ } } -->97 ascll码对应表: http://ascii.911cha.com/
④ nil:{ { print nil } } { {nil } }只有nil会报错:nil is not a command
if:判断
range:循环
注:range也支持else,表示空数组的时候执行
template:对于模板的分模块处理很有用处,引入另一个模板文件
{{template “bottom.html” .}}
注意:如果引入的文件也需要获取动态数据,那{ {template “bottom.html” .} }中必须使用.访问当前位置的上下文
3.2 常用模板函数and
{ {and .X .Y .Z} }
只要有一个为空,则整体为空,如果都不为空,则返回最后一个
or
{ {or .X .Y .Z} }
只要有一个不为空,则返回第一个不为空的,否则返回空
index:读取指定类型对应下标的值
获取
后端:
this.Data["data_map"] = map[string]string{"name": "Beego"}
this.Data["arrs"] = []int{1,2,3,4,5}
前端:
{{index .data_map "name"}}
{{index .arrs 1}}
len:返回对应类型的长度
{ { .Content|len } }
not:返回输入参数的否定值
{{not .is_ok }}
6.eq / ne / lt / le / gt / ge
eq:等于 equlsne:不等于lt:小于 lessle:小于等于 区别于ltegt:大于 greaterge:大于等于 区别于gte内置模板函数
1.date 格式化时间
{{date .T “Y-m-d H:i:s”}}
compare 比较
{{compare 'a' 97}}
substr 截取字符串
{{substr "我是中国人" 0 4}}
map_get 获取map的指定key值
{{ map_get .m "a" }}
url_for
{{urlfor "TestController.List"}}
3.3 自定义模板函数
第一步:定义函数
func hello(in string)(out string){
out = in + "world"
return
}
第二步:AddFuncMap
// 在beego.Run()之前
beego.AddFuncMap("hi",helle)
第三步:前端使用
{{"hello world" | hi}}
四、controller模块
4.1 配置文件的使用
4.1.1 读取配置
beego.AppConfig.String("mysqluser")
4.2.2 引入其他配置文件
app.conf是主配置文件。其他的配置文件需要再app.conf中引入
include "mysql_server.conf"
4.3.3 常用的配置
ViewsPath:模板路径,默认值是 views。
StaticDir:静态文件目录设置,默认是static
sessionon:session 是否开启,默认是 false
httpport:监听端口
EnableXSRF:EnableXSRF
XSRFExpire:XSRF 过期时间,默认值是 0,不过期。
MaxMemory:文件上传默认内存缓存大小,默认值是1 << 26(64M)。
RunMode:应用的运行模式,可选值为prod,dev或者test. 默认是dev, 为开发模式
4.2 路由详解 4.2.1 固定路由一个固定的路由,一个控制器,然后根据用户请求方法不同请求控制器中对应的方法
beego.Router("/hello", &controllers_user.Hello{})
4.2.2 正则路由
beego.Router(“/api/?:id”, &controllers.RController{})
beego.Router(“/api/?:id:int”, &controllers.RController{})
:id([0-9]+) 或者 :id([\d]+) 或者 :id:int
:username([\w]+) 或者 :username:string
4.2.3 自动路由
注册路由的时候不需要指定url,只需要注册控制器即可
beego.AutoRouter(&controllers.UserController{})
规则:
/控制器名/方法名/后面的都是参数。。。
如:/user/get/123/456
4.2.4 自定义路由
注册路由的时候可以指定第三个参数,这个参数就是用来自定义路由的
用法: method :函数名
post:Login post请求的时候访问Login函数
get:User get请求的时候访问User函数
*:LoginOut 所有的请求方法都访问LoginOut函数
4.3 自定义过滤器
需要登录才能访问的
/cms/user/add
/cms/user/list
/cms/user/edit
/cms/book/add
不需要登录可以访问:
/
/login
/register
beego.InsertFilter(pattern string, position int, filter FilterFunc, params …bool)
第一个参数表示过滤的路由规则,支持通配符第二个参数就是过滤器的位置,beego支持的有5种BeforeStatic 静态地址之前BeforeRouter 寻找路由之前BeforeExec 找到路由之后,开始执行相应的 Controller 之前AfterExec 执行完 Controller 逻辑之后执行的过滤器FinishRouter 执行完逻辑之后执行的过滤器第三个参数为执行的函数func(*context.Context) 参数必须是context.Context , context是beego包下的其他参数:使用默认得到true即可示例:
func main() {
// 过滤器
beego.InsertFilter("/*", beego.BeforeRouter, controllers.LoginSpider1Filter)
beego.Run()
}
func LoginSpider1Filter(ctx *context.Context) {
// 获取session
id := ctx.Input.Session("spider1_uid")
if id == nil { // 说明未登录
ctx.Redirect(302,"/main/spider1/to_login")
// 路由反转:beego.URLFor("TestController.Get")
}
}
注意:使用 session 的 Filter 必须在 BeforeStatic 之后才能获取,因为 session 没有在这之前初始化。
4.4 表单数据验证 4.4.1 安装go get github.com/astaxie/beego/validation
4.4.2 使用
结构体定义tag
验证函数写在 “valid” tag 的标签里各个函数之间用分号 “;” 分隔,分号后面可以有空格参数用括号 “()” 括起来,多个参数之间用逗号 “,” 分开,逗号后面可以有空格正则函数(Match)的匹配模式用两斜杠 “/” 括起来各个函数的结果的 key 值为字段名.验证函数名type LoginParams struct {
Name string valid:"Required"
Age int valid:"Required;MinSize(2)"
Addr string valid:"Required"
}
func (l *LoginController) Post() {
valid := validation.Validation{}
// 解析到结构体
params := LoginParams{}
if err := l.ParseForm(¶ms); err != nil {
//handle error
return
}
//重写错误信息:validation.SetDefaultMessage(map)
var messages = map[string]string{
"Required": "不能为空",
"MinSize": "最短长度为 %d",
"Length": "长度必须为 %d",
"Numeric": "必须是有效的数字",
"Email": "必须是有效的电子邮件地址",
"Mobile": "必须是有效的手机号码",
}
validation.SetDefaultMessage(messages)
// 校验
b, err := valid.Valid(¶ms)
// 验证StructTag 是否正确
if err != nil {
fmt.Println(err)
}
if !b {
// 验证没通过,则b为false
for _, err := range valid.Errors {
fmt.Println(err.Key, err.Message)
message := err.Key + err.Message
l.Ctx.WriteString(message)
}
}
}
4.5 session和cookie
4.5.1 什么是session?
Session是在_无状态_的HTTP协议下,服务端记录用户状态时用于标识具体用户的机制它是在服务端保存的用来跟踪用户的状态的数据结构,可以保存在文件、数据库或者集群中在浏览器关闭后这次的Session就消失了,下次打开就不再拥有这个Session。其实并不是Session消失了,而是Session ID变了,
4.5.2 什么是cookie?
Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息每次HTTP请求时,客户端都会发送相应的Cookie信息到服务端。它的过期时间可以任意设置,如果你不主动清除它,在很长一段时间里面都可以保留着,即便这之间你把电脑关机了。每次客户端发请求的时候会自动携带该域名下的Cookie,不用域名间的Cookie是不能共享的
4.5.3 session和cookie:
Cookie 在客户端(浏览器),Session 在服务器端。Cookie的安全性一般,他人可通过分析存放在本地的Cookie并进行Cookie欺骗。在安全性第一的前提下,选择Session更优。重要交互信息比如权限等就要放在Session中,一般的信息记录放Cookie就好了。单个Cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个Cookie。Session 可以放在 文件、数据库或内存中用户验证这种场合一般会用 Session。因此,维持一个会话的核心就是客户端的唯一标识,即Session ID。Session 的运行依赖Session ID,而 Session ID 是存在 Cookie 中的,也就是说,如果浏览器禁用了 Cookie,Session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 Session ID)
4.5.4 beego中使用session:
开启sessionmain.go中开启:beego.BConfig.WebConfig.Session.SessionOn = true配置文件中开启:sessionon = true使用sessio设置sessionSetSession(name string, value interface{})this.SetSession(“username”, “zhiliao”)获取sessionGetSession(name string) interface{}this.GetSession(“username”)删除sessionthis.DelSession(name string)在过滤器中获取sessionctx.Input.Session(“user_name”)关于session的一些其他配置设置是否开启 Sessionbeego.BConfig.WebConfig.Session.SessionOn默认为false设置 Session 过期的时间beego.BConfig.WebConfig.Session.SessionGCMaxLifetime默认值是 3600 秒设置 cookie 的过期时间beego.BConfig.WebConfig.Session.SessionCookieLifeTime设置sessionid加密算法beego.BConfig.WebConfig.Session.SessionHashFunc默认值为 sha1修改sessionkeybeego.BConfig.WebConfig.Session.SessionHashKey默认的 key 是 beegoserversessionkey,建议在使用的时候修改该参数设置 cookies 的名字beego.BConfig.WebConfig.Session.SessionNameSession 默认是保存在用户的浏览器 cookies 里面的,默认名是 beegosessionID,配置文件对应的参数名是:sessionname。
五、model模块
5.1 orm的使用
5.1.1 mysql安装
用户名和密码的配置
5.1.2 navicat安装
5.1.3 beego中使用
安装:
go get github.com/astaxie/beego/orm
go get github.com/go-sql-driver/mysql
连接数据库:
orm.RegisterDriver(“mysql”, orm.DRMySQL)
orm.RegisterDataBase(“default”, “mysql”, “用户名:密码@tcp(IP:端口号)/数据库?charset=utf8”, 30)
参数一:数据库的别名,用来在 ORM 中切换数据库使用
参数二:驱动名称
参数三:对应的链接字符串
参数四(可选):设置最大空闲连接
或根据数据库别名设置:orm.SetMaxIdleConns(“default”, 30)
参数五(可选):设置最大数据库连接
或根据数据库别名设置: orm.SetMaxOpenConns(“default”, 30)
注册模型
在init函数中:orm.RegisterModel(new(Article)),只有注册了模型才可以使用
创建表
在数据库中 *** 作创建表即可,后面会讲自动迁移表
时区设置:
orm.DefaultTimeLoc = time.UTC // 设置为 UTC 时间
ORM 在进行 RegisterDataBase 的同时,会获取数据库使用的时区,然后在 time.Time 类型存取时做相应转换,以匹配时间系统,从而保证时间不会出错
示例代码
首先创建好数据库和表,表对应结构体中的属性
app.conf链接信息配置:
dbhost = 127.0.0.1
dbuser = root
dbpassword = root
dbport = 3306
db = user
main.go中初始化链接:
orm.RegisterDriver(“mysql”, orm.DRMySQL)orm.RegisterDataBase(“default”, “mysql”, “root:root@/orm_test?charset=utf8”)"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
func init() {
// 读取配置信息
dbhost := beego.AppConfig.String("dbhost")
dbport := beego.AppConfig.String("dbport")
dbuser := beego.AppConfig.String("dbuser")
dbpassword := beego.AppConfig.String("dbpassword")
db := beego.AppConfig.String("db")
//注册mysql Driver
orm.RegisterDriver("mysql", orm.DRMySQL)
//构造conn连接
//用户名:密码@tcp(url地址)/数据库
conn := dbuser + ":" + dbpassword + "@tcp(" + dbhost + ":" + dbport + ")/" + db + "?charset=utf8"
//注册数据库连接
orm.RegisterDataBase("default", "mysql", conn)
fmt.Printf("数据库连接成功!%s\n", conn)
}
模型结构体:
注意:结构体的字段名首字母必须大写
type Article struct {
Id int
Name string
Author string
}
func init(){
orm.RegisterModel(new(Article))
}
增加数据:
o := orm.NewOrm()
o.Using("default") // default可以不用using
newStudent := Student{Name:"zhiliao",Age:18,Gender:"男"}
//新增数据
id, err :=o.Insert(&newStudent) // 返回添加的数据id以及错误信息
注意:
ORM 必须注册一个别名为 default 的数据库,作为默认使用 5.2 模型定义用作数据库数据转换和自动建表
5.2.1 模型名和表名映射模型名和表名的映射规则:除了开头的大写字母以外,遇到大写会增加 _,原名称中的下划线保留。
① Article -> article
② AuthUser -> auth_user
③ Auth_User -> auth__user 两个下划线
④DB_AuthUser -> d_b__auth_user
自定义表名
type User struct {
Id int
Name string
}
func (u *User) TableName() string {
return "auth_user"
}
5.2.2 设置参数:使用structtag
忽略字段structtag:`orm:"-"`
pk设置为主键,适用于自定义其他类型为主键
null:Name string `orm:“null”`
auto:自增长
Id int orm:"pk;auto"
index:索引
Name string orm:"index"
索引的作用:优化查询,相当于图书的目录
unique
Name string orm:"unique"
column
Name string orm:"column(user_name)"
size:对string起作用
Title string orm:"size(60)"
digits / decimals 4 2 12.21
Money float64 orm:"digits(12);decimals(4)"
总长度12,小数点后有4位小数
auto_now / auto_now_add
Created time.Time orm:"auto_now_add;type(datetime)"
Updated time.Time orm:"auto_now;type(datetime)"
auto_now 每次 model 保存时都会对时间自动更新
auto_now_add 第一次保存时才设置时间
type:日期还是时间
Created time.Time orm:"auto_now_add;type(date)"
Created time.Time orm:"auto_now_add;type(datetime)"
default
Gender float64 orm:"default(0)"
description
修改源码:cmd_utils.go的 getColumnAddQuery()最后加:fi.description,
Name string orm:"description(这是标签名称)"
一对一
① User模型中: Profile *Profile `orm:“rel(one)”` 正向关系,有外键字段
② Profile模型中: User *User `orm:“reverse(one)”` 反向关系,只是关系,没有外键
一对多
① article模型中:User *User `orm:“rel(fk)”` 正向关系,有外键,外键在多的一方
② user模型中:Articles []*Articles `orm:“reverse(many)” 反向关系,没有外键
一对多,外键在多的一方
多对多
① post模型中:Tags []*Tag `orm:“rel(m2m)”` 正向关系,没有外键
② tag模型中:Posts []*Post `orm:“reverse(many)”` 反向关系,没有外键
5.3 exper表达式前提:注册模型:RegisterModel
5.3.1 使用QueryTable1.
o := orm.NewOrm\(\)
qs := o.QueryTable\("user"\) 表名作为参数
2.
qs := o.QueryTable\(new\(User\)\) 也可以直接使用对象作为表名
返回的是QuerySeter
5.3.2 exper表达式的使用:两个下换线
exact / iexact:等于,默认值,大小写敏感 / 不敏感
qs.Filter("name__exact","Zhiliao").One(&stu)
contains / icontains:包含,大小写敏感 / 不敏感
qs.Filter("name__contains","Zhil").One(&stu)
gt / gte:大于/大于等于
qs.Filter("age__gt",18).One(&stu)
lt / lte:小于/小于等于
qs.Filter("age__lt",18).One(&stu)
startswith / istartswith:以…起始,大小写敏感 / 不敏感
qs.Filter("name__startswith","Zh").One(&stu)
qs.Filter("name__istartswith","Zh").One(&stu)
endswith / iendswith:以…结束,大小写敏感 / 不敏感
qs.Filter("name__endswith","Liao").One(&stu)
qs.Filter("name__iendswith","Liao").One(&stu)
in:在某个范围中,值为不定长参数
qs.Filter("age__in",12,13,16,19).One(&stu)
isnull:为空,值为 true / false
qs.Filter("gender__isnull",true).One(&stu)
查看orm执行的sql语句:
只作用于当前的模型:当前模型的init中orm.Debug = true作用于所有模型:beego.Run()前面设置orm.Debug = true 5.4 QueryTable接口One:一条数据
qs.Filter(“name__exact”,“Zhiliao”).One(&stu)指定显示的字段qs.Filter(“name__exact”,“Zhiliao”).One(&stu,“name”,“age”)Filter:包含条件
多个 Filter 之间使用 AND 连接qs.Filter(“profile__isnull”, true).Filter(“name”, “slene”)类sql: WHERE NOT profile_id IS NULL AND name = ‘slene’All:所有数据
返回对应的结果集对象默认最大行数为 1000user_arr := []models.UserStruct{}qs.Filter(“name__exact”,“Zhiliao”).All(&user_arr)指定显示的字段qs.Filter(“name__exact”,“Zhiliao”).All(&user_arr,“name”,“age”)Exclude:排除条件
Exclude 之间使用 AND 连接使用 NOT 排除条件qs.Exclude(“profile__isnull”, true).Filter(“name”, “slene”)Limit:限制最大返回数据行数
第二个可选参数可以设置Offset,意思从什么位置开始查询qs.Limit(10, 20)LIMIT 10 OFFSET 20 注意跟 SQL 反过来的beego中limit的使用:qs.Limit(2,1).All(&user_arr)sql的limit:limit 0,2; 从第0个数据开始,查询两条数据Offset:设置 偏移行数,意思从什么位置开始查询
qs.Offset(20)beego中使用:qs.Filter(“name__isnull”,false).Offset(1).All(&user_arr)角标从0开始,代表第一条数据GroupBy:分组
根据“By”指定的规则对数据进行分组beego中使用GroupBy:qs.GroupBy(“age”).All(&user_arr)sql:SELECT SUM(age) FROM `user` GROUP BY addrgroup by 必须放在 order by 和 limit之前OrderBy:排序
参数使用 exprASC升序,字段名前面没有- :qs.OrderBy(“age” ) 默认的DESC降序,字段名前面有- :qs.OrderBy("-age" )根据多个字段排序,用逗号隔开: qs.OrderBy("-age" ,“name”)qs.OrderBy("-age").All(&user_arr)Distinct:去重,没有参数
根据指定字段去重,需要在all方法中指定字段,否则根据所有字段去重beego中的使用:qs.Distinct().All(&user_arr,“name”,“age”,“addr”)sql:SELECT DISTINCT addr, age,name FROM `user`Count:统计个数,没有参数,后面不能跟查询条数的方法
num, err := qs.Filter(“name__isnull”,false).Count()统计的个数错误信息sql:SELECT COUNT(*) FROM `user`;Exist:是否存在,没有参数,后面不能跟查询条数的方法
exict := qs.Filter(“name__isnull”,false).Exist()返回true或falseUpdate:更新
依据当前查询条件,进行批量更新 *** 作num, err := o.QueryTable(“user”).Filter(“name”, “slene”).Update(orm.Params{“name”: “astaxie”,})返回更新的条数和错误信息Delete:删除
依据当前查询条件,进行批量删除 *** 作num, err := o.QueryTable(“user”).Filter(“name”, “slene”).Delete()返回删除的条数和错误信息补充:不是QueryTable接口
insert:插入
前提:
数据库已注册:orm.RegisterDataBase模型已注册:orm.RegisterModel 5.5.1 在main.go中调用RunCommand命令beego.Run()命令之前调用:
orm.RunCommand()
5.5.2 cmd中执行命令
go run main.go orm syncdb -h
syncdb参数:
db:指定数据库别名,默认使用别名为 defaultforce 删除表后再创建,默认为truev 可以查看执行的 sql 语句查看执行的sql语句
go run main.go orm sqlall
5.5.3 在程序中调用直接自动建表
name := "default"
// drop table 后再建表
force := false
// 打印执行过程
verbose := true
// 遇到错误立即返回
err := orm.RunSyncdb(name, force, verbose)
if err != nil {
fmt.Println(err)
}
六、日志模块
6.1 日志模块的使用
下载包:go get github.com/astaxie/beego/logs
6.1.1 使用引入包:import “github.com/astaxie/beego/logs”
添加输出引擎:logs.SetLogger(“console”)
打日志:logs.Debug(“this is debug”)
SetLogger参数:
第一个参数:引擎第二个参数:日志配置信息,字符串,不同的引擎参数不一样{"filename":"project.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10,"color":true}
1:LevelEmergency
2:LevelAlert
3:LevelCritical
4:LevelError
5:LevelWarn = LevelWarning
6:LevelNotice
7:LevelInfo = LevelInformational
8:LevelTrace = LevelDebug
6.1.3引擎console
logs.SetLogger(logs.AdapterConsole, {"level":1,"color":true}
)
file
输出到文件
logs.SetLogger(logs.AdapterFile, {"filename":"test.log"}
)
参数:
① filename 保存的文件名
② maxlines 每个文件保存的最大行数,默认值 1000000
③ maxsize 每个文件保存的最大尺寸,默认值是 1 << 28, //256 MB 2^28 1<< 3 2^3
④ daily 是否按照每天 logrotate,默认是 true
⑤ maxdays 文件最多保存多少天,默认保存 7 天
⑥ rotate 是否开启 logrotate,默认是 true
⑦ level 日志保存的时候的级别,默认是 Trace 级别
⑧ perm 日志文件权限 4(读权限)2(写权限)1(执行权限)
multifile
多文件日志写入,对号入座写入,比如test.error.log,err.debug.log
logs.SetLogger(logs.AdapterMultiFile, {"filename":"test.log","separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"]}
)
smtp
邮件发送
logs.SetLogger(logs.AdapterMail,{"username":"xxx@qq.com","password":"认证密码","host":"smtp.qq.com:587","fromAddress":"xxx@qq.com","sendTos":["xxx@qq.com"]}
)
…
七、实战 7.1 实战介绍 7.1.1 后端:登录,帖子管理
7.1.2 前端:登录、注册、帖子评论(一级,二级评论)
八、补充 8.1 beego中使用富文本编辑器引入css:前两个必须引入
<link rel="stylesheet" href=" /static/froala_editor/css/froala_editor.css">
<link rel="stylesheet" href="/static/froala_editor/css/froala_style.css">
<link rel="stylesheet" href="/static/froala_editor/css/plugins/code_view.css">
<link rel="stylesheet" href="/static/froala_editor/css/plugins/image_manager.css">
<link rel="stylesheet" href="/static/froala_editor/css/plugins/image.css">
<link rel="stylesheet" href="/static/froala_editor/css/plugins/table.css">
<link rel="stylesheet" href="/static/froala_editor/css/plugins/video.css">
引入js:第一个必须引入
<script type="text/javascript" src="/static/froala_editor/js/froala_editor.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/align.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/code_beautifier.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/code_view.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/draggable.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/image.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/image_manager.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/link.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/lists.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/paragraph_format.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/paragraph_style.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/table.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/video.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/url.min.js">script>
<script type="text/javascript" src="/static/froala_editor/js/plugins/entities.min.js">script>
使用:
<textarea id="content" placeholder="请输入新闻内容">textarea>
var editor = new FroalaEditor("#content",{ // 注意设置的id
toolbarButton:['bold','italic'],
language:'zh_cn',
width: 500,
height:100,
toolbarButtons: ['fullscreen', 'bold', 'italic', 'underline', 'strikeThrough', 'subscript', 'superscript', 'fontFamily', 'fontSize', '|', 'color', 'emoticons', 'inlineStyle', 'paragraphStyle', '|', 'paragraphFormat', 'align', 'formatOL', 'formatUL', 'outdent', 'indent', '-', 'insertLink', 'insertImage', 'insertTable', '|', 'quote', 'insertHR', 'undo', 'redo', 'clearFormatting', 'selectAll', 'html'],
quickInsertButtons: ['image', 'table', 'ol', 'ul']
// imageUploadURL: "http://192.168.1.86:8080/exampage/ques/ziliaoceshi.do",
});
参数设置:
placeholderText: '请输入内容', //默认填充内容
charCounterCount: true,//默认 显示字数
saveInterval: 0,//不自动保存,默认10000,0为不自动保存
width: 450, 宽度
height:500, 高度
charCounterMax: 3000, //最大字数限制,-1为不限制
imageUploadMethod:图片上传请求方式
imageUploadURL:图片上传路径
imageDefaultWidth: 200, //上传图片后的默认大小
imageUploadParams: {aid: '1'}, //上传图片时带的参数
language: 'zh_cn',
videoUploadURL:视频上传路径
toolbarButtons: ['bold', 'italic', 'underline', 'paragraphFormat', 'align','color','fontSize','insertImage','insertTable','undo', 'redo']
获取内容:
events: {
initialized: function () {
const editor = this;
this.el.closest('form').addEventListener('submit', function (e) {
console.log(editor.$oel.val());
e.preventDefault()
})
}
}
8.2 beego中使用消息通知
需不需要表?
xx用户,你借的车辆已经预期,请尽快归还公司重大通知。消息通知模块只是用来展示车辆逾期的通知嘛?
不只是用来接受逾期的消息表设计
字段名称 | 类型 | 长度 | 说明 |
---|---|---|---|
id | int | 11 | 主键,自增长 |
uid | int | 11 | 用户id外键 |
flag | int | 11 | 1:车辆逾期,2:所有通知 |
title | string | 64 | 消息标题 |
content | longtext | 0 | 消息内容 |
read_tag | int | 11 | 1:已读,0:未读 |
当前日期大于车辆归还日期,就表示逾期,当逾期了就应该保存一条数据到通知表中
重点:怎么触发这个逻辑部分的判断??
归还日期:2020-08-15
定时:在指定的时间执行某个逻辑
8.3 login模块user模型设计:
字段名称 | 类型 | 长度 | 说明 |
---|---|---|---|
id | int | 11 | 自增长id |
user_name | string | 64 | 用户名 |
password | string | 32 | 6-32长度的密码 |
age | int | 11 | 年龄 |
gender | string | 2 | 1:男 2:女 3:未知 性别 |
phone | bigint | 11 | 电话号码 |
addr | string | 255 | 地址 |
is_active | int | 2 | 1:启用,0:停用 |
is_delete | int | 2 | 1:删除,0:未删除 |
create_time | datetime | 创建时间 | |
number | string | 64 | 工号 |
real_name | string | 255 | 真实姓名 |
验证码:
go get github.com/mojocn/base64Captcha 生成验证码借助于base64Captcha需要goroot目录下的src中添加我提供的golang.org文件夹,解压后注意不要嵌套,这个是image/font的依赖包欢迎分享,转载请注明来源:内存溢出
评论列表(0条)