- 添加和查询
charlie = User.create(username='charlie')
huey = User(username='huey')
huey.save()
Tweet.create(user=charlie, message='My first tweet')
data_source = [
{'field1': 'val1-1', 'field2': 'val1-2'},
{'field1': 'val2-1', 'field2': 'val2-2'},
]
for data_dict in data_source:
Model.create(**data_dict)
User.get(User.username == 'charlie')
usernames = ['charlie', 'huey', 'mickey']
users = User.select().where(User.username.in_(usernames))
tweets = Tweet.select().where(Tweet.user.in_(users))
tweets = (Tweet
.select()
.join(User)
.where(User.username.in_(usernames)))
tweets_today = (Tweet
.select()
.where(
(Tweet.created_date >= datetime.date.today()) &
(Tweet.is_published == True))
.count())
User.select().order_by(User.username).paginate(3, 20)
tweet_ct = fn.Count(Tweet.id)
users = (User
.select(User, tweet_ct.alias('ct'))
.join(Tweet, JOIN.LEFT_OUTER)
.group_by(User)
.order_by(tweet_ct.desc()))
- 更新删除
User.update(age=20).where(User.username=="charlie").execute()
Counter.update(count=Counter.count + 1).where(Counter.url == request.url)
user = User.get(User.id == 1)
user.delete_instance()
query = Tweet.delete().where(Tweet.creation_date < one_year_ago)
query.execute()
- 更多功能
class BaseModel(Model):
add_time = DateTimeField(default=datetime.datetime.now, verbose_name="添加时间")
class Meta:
database = db
class Person(BaseModel):
name = CharField()
p_id = Person.insert({'name': 'bobby'}).execute()
data = [
{'facid': 9, 'name': 'Spa', 'membercost': 20, 'guestcost': 30,'initialoutlay': 100000, 'monthlymaintenance': 800},
{'facid': 10, 'name': 'Squash Court 2', 'membercost': 3.5,'guestcost': 17.5, 'initialoutlay': 5000, 'monthlymaintenance': 80}]
query = Facility.insert_many(data)
class Person(Model):
first = CharField()
last = CharField()
class Meta:
primary_key = CompositeKey('first', 'last')
class Pet(Model):
owner_first = CharField()
owner_last = CharField()
pet_name = CharField()
class Meta:
constraints = [SQL('FOREIGN KEY(owner_first, owner_last) REFERENCES person(first, last)')]
- 还有很多查询方法:模糊、条件、多表、聚合;再议吧,都是MySQL的基础操作
- go语言web框架,类似python的flask;go语言还有一个对标Django的beego框架;学习的话建议互补,没必要重复学两个轻量/重量frame
- 安装:
go get -u github.com/gin-gonic/gin
,如果使用go module,无需安装,自动同步 - helloworld
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run()
}
import (
"github.com/gin-gonic/gin"
"net/http"
)
func pong(c *gin.Context) {
c.JSON(http.StatusOK, map[string]string{"message": "pong"})
}
func main() {
r := gin.Default()
r.GET("/ping", pong)
r.Run(":8083")
}
- 可以下载解压Chrome的扩展程序jsonview,方便查看
- URL配置
func main() {
router := gin.Default()
router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)
router.Run()
}
- 路由分组,将前缀相同的路由合并
- 路由参数
r.GET("/user/:name/:action/", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
c.String(http.StatusOK, "%s is %s", name, action)
})
import (
"github.com/gin-gonic/gin"
"net/http"
"testing"
)
func goodsList(c *gin.Context) {
c.JSON(http.StatusOK, map[string]string{"message": "list"})
}
func goodsDetail(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
c.String(http.StatusOK, "%s is %s", name, action)
}
func goodsCreate(c *gin.Context) {
c.JSON(http.StatusOK, map[string]string{"message": "add"})
}
func TestParams(t *testing.T) {
router := gin.Default()
goodsGroup := router.Group("/goods")
{
goodsGroup.GET("/:name", goodsDetail)
goodsGroup.GET("/list", goodsList)
goodsGroup.POST("/list", goodsCreate)
}
router.Run(":8083")
}
r.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
c.String(http.StatusOK, "%s is %s", name, action)
})
- 匹配指定类型的路由参数
type Person struct {
ID int`uri:"id" binding:"required,uuid"`
Name string `uri:"name" binding:"required"`
}
func main() {
route := gin.Default()
route.GET("/:name/:id", func(c *gin.Context) {
var person Person
if err := c.ShouldBindUri(&person); err != nil {
c.JSON(400, gin.H{"msg": err})
return
}
c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
})
route.Run(":8088")
}
- 上面是路由参数(路径里的),这里将怎么获取get/post的参数
- GET参数又叫查询字符串参数,使用
Query
方法func TestParams2(t *testing.T) {
router := gin.Default()
router.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "Guest")
lastname := c.Query("lastname")
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
router.Run(":8080")
}
- POST参数,使用requests发起请求;这个demo里:传递什么返回什么
func TestParams3(t *testing.T) {
router := gin.Default()
router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
router.Run(":8080")
}
import requests
rsp = requests.post("http://127.0.0.1:8083/form_post", data={
"message": "Hello",
"nick": "Roy"
})
print(rsp.text)
- 混合获取,发起post请求
func TestParams4(t *testing.T) {
router := gin.Default()
router.POST("/post", func(c *gin.Context) {
id := c.Query("id")
page := c.DefaultQuery("page", "0")
name := c.PostForm("name")
message := c.PostForm("message")
fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
})
router.Run(":8083")
}
- 返回JSON不一定非要
gin.H
,可以使用struct,还能配合json-tag,改变key名称func TestParams5(t *testing.T) {
r := gin.Default()
r.GET("/someJSON", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/moreJSON", func(c *gin.Context) {
var msg struct {
Name string `json:"user"`
Message string
Number int
}
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
c.JSON(http.StatusOK, msg)
})
r.Run(":8080")
{
user: "Lena",
Message: "hey",
Number: 123
}
}
- 通过protobuf传递请求参数和函数返回值,就是序列化数据的方式变了,gin也直接支持protobuf:
c.ProtoBuf()
// user.proto
syntax = "proto3";
option go_package = ".;proto";
message Teacher {
string name = 1;
repeated string course = 2;
}
// 生成go文件
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"start/gin_t/proto"
)
func main() {
r := gin.Default()
r.GET("/someProtoBuf", func(c *gin.Context) {
courses := []string{"python", "django", "go"}
data := &proto.Teacher{
Name: "Roy",
Course: courses,
}
c.ProtoBuf(http.StatusOK, data)
})
r.Run(":8083")
}
- 和flask类似,gin需要集成表单验证的框架,官方文档,具体怎么限制都可以参考这里
- 用起来也比较简单,使用struct定义字段的验证规则,控制器函数用
ShouldBind
方法进行Form验证
type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
func main() {
router := gin.Default()
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if json.User != "manu" || json.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
_ = router.Run(":8083")
})
}
import requests
rsp = requests.post("http://127.0.0.1:8083/login", json={
"user":"Roy",
"pas":"123"
})
print(rsp.text)
- 来个注册的验证
type SignUpParam struct {
Age uint8 `json:"age" binding:"gte=1,lte=130"`
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required"`
RePassword string `json:"re_password" binding:"required,eqfield=Password"`
}
func TestParams7(t *testing.T) {
router := gin.Default()
router.POST("/signup", func(c *gin.Context) {
var json SignUpParam
if err := c.ShouldBind(&json); err != nil {
c.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
}
c.JSON(http.StatusOK, gin.H{"status": "you are signed up"})
})
_ = router.Run(":8083")
}
rsp = requests.post("http://127.0.0.1:8083/signup", json={
"age": 18,
"name": "Roy",
"email": "ss2@qq.com",
"pas": "123",
"re_password": "123"
})
print(rsp.text)
- 但是上面返回的信息都是英文,因为validator支持国际语言,可以配置成中文;这里还是借助第三方包实现gin的翻译interface,定制很方便
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translation "github.com/go-playground/validator/v10/translations/en"
zh_translation "github.com/go-playground/validator/v10/translations/zh"
"net/http"
"testing"
)
var trans ut.Translator
func InitTrans(locale string) (err error) {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
zhT := zh.New()
enT := en.New()
uni := ut.New(enT, zhT, enT)
trans, ok = uni.GetTranslator(locale)
if !ok {
return fmt.Errorf("%s", locale)
}
switch locale {
case "en":
en_translation.RegisterDefaultTranslations(v, trans)
case "zh":
zh_translation.RegisterDefaultTranslations(v, trans)
default:
en_translation.RegisterDefaultTranslations(v, trans)
}
return
}
return err
}
func TestParams7(t *testing.T) {
if err := InitTrans("zh"); err != nil {
fmt.Println("初始化翻译器错误")
return
}
router := gin.Default()
router.POST("/signup", func(c *gin.Context) {
var json SignUpParam
if err := c.ShouldBind(&json); err != nil {
errs, _ := err.(validator.ValidationErrors)
c.JSON(http.StatusBadRequest, gin.H{
"msg": errs.Translate(trans),
})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are signed up"})
})
_ = router.Run(":8083")
}
- 修改返回的json格式(json格式化),还是在InitTrans定义,这部分类似中间件,当使用了我们实现了接口的自定义的翻译器时,就会触发这里注册的一系列操作
func TestParams7(t *testing.T) {
if err := InitTrans("zh"); err != nil {
fmt.Println("初始化翻译器错误")
return
}
router := gin.Default()
router.POST("/signup", func(c *gin.Context) {
var json SignUpParam
if err := c.ShouldBind(&json); err != nil {
errs, _ := err.(validator.ValidationErrors)
c.JSON(http.StatusBadRequest, gin.H{
"msg": remove(errs.Translate(trans)),
})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are signed up"})
})
_ = router.Run(":8083")
}
var trans ut.Translator
func InitTrans(locale string) (err error) {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterTagNameFunc(func(field reflect.StructField) string {
name := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
zhT := zh.New()
enT := en.New()
uni := ut.New(enT, zhT, enT)
trans, ok = uni.GetTranslator(locale)
if !ok {
return fmt.Errorf("%s", locale)
}
switch locale {
case "en":
en_translation.RegisterDefaultTranslations(v, trans)
case "zh":
zh_translation.RegisterDefaultTranslations(v, trans)
default:
en_translation.RegisterDefaultTranslations(v, trans)
}
return
}
return err
}
func remove(fields map[string]string) map[string]string {
rsp := map[string]string{}
for field, err := range fields {
rsp[field[strings.Index(field, ".")+1:]] = err
}
return rsp
}
- 静态资源(图片、样式、html文件等)通过模板文件解决,官方文档
- 其实就两个问题,(1) 拿到静态文件,(2) 将变量填充进去;最后返回给客户端
func main() {
r := gin.Default()
r.LoadHTMLFiles("templates/index.tmpl")
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "RoyKun",
})
})
r.Run(":8083")
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{.title}}</h1>
</body>
</html>
- 但此时会报HTTP ERROR 500错误,模板文件找不到;这是因为我们的代码运行底层要编译
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
fmt.Println("curr:", dir)
- 解决办法就是,(1) 使用绝对路径,(2) 在terminal执行main.exe启动server
- 推荐第二种方式,这也是服务上线后启动的方式:
go build main.go
- 当有多个静态文件时,使用LoadHTMLFiles有点力不从心,可以用
LoadHTMLGlob
,并使用{{define "name"}}
(起别名)对应逻辑函数中的name
,唯一定位模板文件func main() {
r := gin.Default()
r.LoadHTMLGlob("templates/**/*")
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "myindex.html", gin.H{
"title": "index",
})
})
r.GET("/goods", func(c *gin.Context) {
c.HTML(http.StatusOK, "goods/list.html", gin.H{
"title": "gets/goods",
})
})
r.GET("/users", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/list.html", gin.H{
"title": "gets/users",
})
})
r.Run(":8083")
}
{{define "myindex.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{.title}}</h1>
</body>
</html>
{{end}}
{{define "goods/list.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品</title>
</head>
<body>
<h1>商品</h1>
</body>
</html>
{{end}}
{{define "users/list.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户</title>
</head>
<body>
<h1>用户</h1>
</body>
</html>
{{end}}
- 上面都是说模板文件,还有其他的静态文件,比如样式、图片、js等,怎么引入呢?
r.StaticFS("/static", http.Dir("./static"))
<link rel="stylesheet" href="/static/demo.css">