Go:Beego写一个博客

时间:May 23, 2019 分类:

目录:

Beego使用

bee工具

安装beego的bee工具

$ go get github.com/astaxie/beego
$ go get github.com/beego/bee

使用bee生成beego项目

$ bee new everyday
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0
2018/11/20 18:26:20 INFO     ▶ 0001 Creating application...
    create   /root/go_code/src/everyday/
    create   /root/go_code/src/everyday/conf/
    create   /root/go_code/src/everyday/controllers/
    create   /root/go_code/src/everyday/models/
    create   /root/go_code/src/everyday/routers/
    create   /root/go_code/src/everyday/tests/
    create   /root/go_code/src/everyday/static/
    create   /root/go_code/src/everyday/static/js/
    create   /root/go_code/src/everyday/static/css/
    create   /root/go_code/src/everyday/static/img/
    create   /root/go_code/src/everyday/views/
    create   /root/go_code/src/everyday/conf/app.conf
    create   /root/go_code/src/everyday/controllers/default.go
    create   /root/go_code/src/everyday/views/index.tpl
    create   /root/go_code/src/everyday/routers/router.go
    create   /root/go_code/src/everyday/tests/default_test.go
    create   /root/go_code/src/everyday/main.go
2018/11/20 18:26:20 SUCCESS  ▶ 0002 New application successfully created!

目录结构

看一下目录结构

$ tree src/everyday/
src/beeblog/
├── conf                //配置文件
│   └── app.conf
├── controllers         //控制器
│   └── default.go      
├── main.go
├── models              //数据库相关
├── routers
│   └── router.go
├── static              //静态文件
│   ├── css
│   ├── img
│   └── js
│       └── reload.min.js
├── tests
│   └── default_test.go
└── views               //模板文件目录
    └── index.tpl

启动beego

$ bee run
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0
2018/11/20 18:33:06 INFO     ▶ 0001 Using 'beeblog' as 'appname'
2018/11/20 18:33:06 INFO     ▶ 0002 Initializing watcher...
2018/11/20 18:33:08 SUCCESS  ▶ 0003 Built Successfully!
2018/11/20 18:33:08 INFO     ▶ 0004 Restarting 'beeblog'...
2018/11/20 18:33:08 SUCCESS  ▶ 0005 './beeblog' is running...
2018/11/20 18:33:08.083 [I] [asm_amd64.s:2361]  http server Running on http://:8080

beego官方文档

模板使用

beego模板语法指南

示例

可以先参考一下生成的文件

views/index.tpl中有以下内容,和django的模板类似

  <footer>
    <div class="author">
      Official website:
      <a href="http://{{.Website}}">{{.Website}}</a> /
      Contact me:
      <a class="email" href="mailto:{{.Email}}">{{.Email}}</a>
    </div>
  </footer>

字段在controllers/default.go中定义

package controllers

import (
        "github.com/astaxie/beego"
)

type MainController struct {
        beego.Controller
}

func (c *MainController) Get() {
        c.Data["Website"] = "beego.me"
        c.Data["Email"] = "astaxie@gmail.com"
        c.TplName = "index.tpl"
}

if判断

{{if true}}
{{else}}
{{end}}

struct

type u struct {
    Name    string
    Age     int
    Sex     string
}
user := &u {
    Name: "Why",
    Age: 24,
    Sex: "Male",
}
nums := []int{1, 2, 3, 4, 5}

定义静态文件目录

beego.SetStaticPath("/static","public")
  • 第一个参数是url路径
  • 第二个参数是静态文件的本地目录

过滤和中间件

var FilterUser = func(w http.ResponseWriter, r *http.Request) { if r.URL.User == nil || r.URL.User.Username() != "admin" { http.Error(w, "", http.StatusUnauthorized) } }

beego.Filter(FilterUser)

编写一个博客——beeblog

beeblog的处理流程是

  1. 直接使用main.go注册路由router或者引入文件方式注册
  2. 然后router到不同的Controller
  3. Controller中创建路由对应名称的crontroller的struct(类型),直接定义以crontroller为参数的Get或者Post方法即可实现,业务逻辑
  4. 通过返回一个data的map返回前端数据和网页,网页在返回给前端之前由beego进行模板渲染,展示给浏览器。

配置

conf/app.conf

appname = beeblog
httpport = 8080
runmode = dev
uname = admin
pwd = admin

model设计

models/models.go

package models

import (
    "os"
    "path"
    "time"
    "github.com/Unknwon/com"
    "github.com/astaxie/beego/orm"
    //只执行初始化函数,不进行其他函数的调用,驱动会在初始化函数中进行注册
    _ "github.com/mattn/go-sqlite3"
)

//常量定义
const (
    //数据库名称
    _DB_NAME = "data/beeblog.db"
    //驱动名称
    _SQLITE3_DRIVER = "sqlite3"
)

//结构定义
type Category struct {
    // Id默认是主键
    Id                int64
    // 文章分类
    Title             string
    // 创建时间
    Created           time.Time `orm: "index"` 
    // 浏览次数
    Views             int64 `orm: "index"`
    // 分类最后一个的创建时间
    TopicTime         time.Time `orm: "index"`
    // 分类文章数量
    TopicCount        int64
    // 最后操作的用户Id
    TopicLastUserId   int64
}

type Topic struct {
    Id                int64
    // 用户Id
    Uid               int64
    // 标题
    Title             string
    // 文本
    Content           string `orm: "size(5000)"`
    // 附件
    Attachment        string
    // 创建时间
    Created           time.Time `orm: "index"`
    // 更新时间
    Updated           time.Time `orm: "index"`
    // 
    Views             int64
    // 作者名称
    Author            string
    // 回复时间
    ReplyTime         time.Time `orm: "index"`
    // 回复数量
    ReplyCount        int64
    // 最后回复的用户Id
    ReplyLastUserId   int64
}

// 主进程调用
func RegisterDB(){
    //判断对比文件是否存在
    if !com.IsExist(_DB_NAME) {
        os.MkdirAll(path.Dir(_DB_NAME), os.ModePerm)
        os.Create(_DB_NAME)
    }

    //注册模型
    orm.RegisterModel(new(Category), new(Topic))
    //注册驱动
    orm.RegisterDriver(_SQLITE3_DRIVER, orm.DRSqlite)
    //注册默认数据库, 默认第一个名称需要为default,然后是驱动,数据库文件,最大连接数
    orm.RegisterDataBase("default", _SQLITE3_DRIVER, _DB_NAME, 10)
}

初始化数据库

main.go

package main

import (
        _ "beeblog/routers"
        "beeblog/controllers"
        "beeblog/models"
        "github.com/astaxie/beego"
        "github.com/astaxie/beego/orm"
)

func init() {
    models.RegisterDB()
}

func main() {
    // 打印相关信息
    orm.Debug = true
    orm.RunSyncdb("default", false, true)
    beego.Router("/", &controllers.MainController{})
    beego.Run()
}

直接启动服务

[root@why 01:32:46 beeblog]go get github.com/Unknwon/com
[root@why 01:33:15 beeblog]go get github.com/mattn/go-sqlite3
[root@why 01:34:34 beeblog]#/root/go/bin/bee run beeblog
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0
2018/08/29 01:34:36 INFO     ▶ 0001 Using 'beeblog' as 'appname'
2018/08/29 01:34:36 INFO     ▶ 0002 Initializing watcher...
beeblog/models
beeblog
2018/08/29 01:34:40 SUCCESS  ▶ 0003 Built Successfully!
2018/08/29 01:34:40 INFO     ▶ 0004 Restarting 'beeblog'...
2018/08/29 01:34:40 SUCCESS  ▶ 0005 './beeblog' is running...
create table `category` 
    -- --------------------------------------------------
    --  Table Structure for `beeblog/models.Category`
    -- --------------------------------------------------
    CREATE TABLE IF NOT EXISTS `category` (
        `id` integer NOT NULL PRIMARY KEY AUTOINCREMENT,
        `title` varchar(255) NOT NULL DEFAULT '' ,
        `created` datetime NOT NULL,
        `views` integer NOT NULL DEFAULT 0 ,
        `topic_time` datetime NOT NULL,
        `topic_count` integer NOT NULL DEFAULT 0 ,
        `topic_last_user_id` integer NOT NULL DEFAULT 0 
    );

create table `topic` 
    -- --------------------------------------------------
    --  Table Structure for `beeblog/models.Topic`
    -- --------------------------------------------------
    CREATE TABLE IF NOT EXISTS `topic` (
        `id` integer NOT NULL PRIMARY KEY AUTOINCREMENT,
        `uid` integer NOT NULL DEFAULT 0 ,
        `title` varchar(255) NOT NULL DEFAULT '' ,
        `content` varchar(255) NOT NULL DEFAULT '' ,
        `attachment` varchar(255) NOT NULL DEFAULT '' ,
        `created` datetime NOT NULL,
        `updated` datetime NOT NULL,
        `views` integer NOT NULL DEFAULT 0 ,
        `author` varchar(255) NOT NULL DEFAULT '' ,
        `reply_time` datetime NOT NULL,
        `reply_count` integer NOT NULL DEFAULT 0 ,
        `reply_last_user_id` integer NOT NULL DEFAULT 0 
    );

2018/08/29 01:34:40.909 [I] [asm_amd64.s:2361]  http server Running on http://:8080

可以看到服务已经被自动创建

创建首页

定义路由vi main.go

package main

import (
        _ "beeblog/routers"
        "beeblog/controllers"
        "beeblog/models"
        "github.com/astaxie/beego"
        "github.com/astaxie/beego/orm"
)

func init() {
    models.RegisterDB()
}

func main() {
    orm.Debug = true
    orm.RunSyncdb("default", false, true)
    // 主页
    beego.Router("/", &controllers.MainController{})
    beego.Run()
}

写路由对应业务逻辑vi controllers/default.go

package controllers

import (
        "github.com/astaxie/beego"
        "beeblog/models"
)

type MainController struct {
        beego.Controller
}

func (this *MainController) Get() {
        this.TplName = "home.html"
}

主页页面vi views/home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="shortcut icon" href="/static/img/favicon.png">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">

    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <link href="https://v3.bootcss.com/assets/js/ie10-viewport-bug-workaround.js" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="https://v3.bootcss.com/examples/starter-template/starter-template.css" rel="stylesheet">

    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]><script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
    <script src="https://v3.bootcss.com/assets/js/ie-emulation-modes-warning.js"></script>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
</head>
<body>
    <nav class="navbar navbar-default navbar-fixed-top">
        <div class="container">
            <a class="navbar-brand" href="/">我的博客</a>
            <div>
                <ul class="nav navbar-nav">
                    <li><a href="/">首页</a></li>
                    <li><a href="/category">分类</a></li>
                    <li><a href="/topic">文章</a></li>
                </ul>
            </div>
        </div>
    </nav>
    <div class="container theme-showcase">
        <div class="page-header">
            <h1>第一篇博客</h1>
            <h6 class="text-muted">文章发表与2018年08月29日</h6>
            <p>
                Hello World!
            </p>
        </div>
    </div>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

再次启动看一下首页

$ /root/go/bin/bee run beeblog

使用模板

头部模板

t_header1.html

{{define "header1"}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="shortcut icon" href="/static/img/favicon.png">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">

    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <link href="https://v3.bootcss.com/assets/js/ie10-viewport-bug-workaround.js" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="https://v3.bootcss.com/examples/starter-template/starter-template.css" rel="stylesheet">

    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]><script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
    <script src="https://v3.bootcss.com/assets/js/ie-emulation-modes-warning.js"></script>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
{{end}}

导航模板

t_container.html

{{define "container"}}
</head>
<body>
    <nav class="navbar navbar-default navbar-fixed-top">
        <div class="container">
            <a class="navbar-brand" href="/">我的博客</a>
            <div>
                <ul class="nav navbar-nav">
                    <li {{if .IsHome}}class="active"{{end}}><a href="/">首页</a></li>
                    <li {{if .IsCategory}}class="active"{{end}}><a href="/category">分类</a></li>
                    <li {{if .IsTopic}}class="active"{{end}}><a href="/topic">文章</a></li>
                </ul>
            </div>
            <div class="pull-right">
                <ul class="nav navbar-nav">
                    {{if .IsLogin}}
                    <li><a href="/login?exit=true">退出</a></li>
                    {{else}}
                    <li><a href="/login">登录</a></li>
                    {{end}}
                </ul>
            </div>
        </div>
    </nav>
{{end}}
  • 通过当前页面位置判断导航栏
  • 通过登录状态判断显示登录或退出

首页使用模板

views/home.html

{{template "header1" .}}
 <title>首页 - 我的beego博客</title>
{{template "container" .}}
    <div class="container theme-showcase">
        <div class="page-header">
            <h1>第一篇博客</h1>
            <h6 class="text-muted">文章发表与2018年08月29日</h6>
            <p>
                Hello World!
            </p>
        </div>
    </div>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

设计登录

主页路由

main.go

package main

import (
        _ "beeblog/routers"
        "beeblog/controllers"
        "beeblog/models"
        "github.com/astaxie/beego"
        "github.com/astaxie/beego/orm"
)

func init() {
    models.RegisterDB()
}

func main() {
    orm.Debug = true
    orm.RunSyncdb("default", false, true)
    beego.Router("/", &controllers.MainController{})
    beego.Router("/login", &controllers.LoginController{})
    // 自动路由,结构必须以Controller结尾
    beego.AutoRouter(&controllers.TopicController{})
    beego.Router("/reply", &controllers.ReplyController{})
    beego.Run()
}

登录前端

views/login.html

{{template "header1" .}}
 <title>首页 - 我的beego博客</title>
</head>
<body>
    <div class="container" style="width: 500px">
        <form method="post" action="/login">
          <div class="form-group">
            <label>Account</label>
            <input id="uname" name="uname" class="form-control" placeholder="Enter Account">
          </div>
          <div class="form-group">
            <label>Password</label>
            <input id="pwd" name="pwd" class="form-control" placeholder="Password">
          </div>
          <div class="checkbox">
            <label>
              <input name="autoLogin" type="checkbox"> 记住登录
            </label>
          </div>
          <button type="submit" class="btn btn-default" onclick="return checkInput();">登录</button>
          <button class="btn btn-default" onclick="return backToHome();">退出</button>
        </form>
    </div>
    <script type="text/javascript">
        function checkInput() {
            var uname = document.getElementById("uname")
            var pwd = document.getElementById("pwd")
            if (uname.value.length == 0) {
                alert("请输入账号");
                return false
            }
            if (pwd.value.length == 0) {
                alert("请输入密码");
                return false
            }
            return true
        }
        function backToHome() {
            window.location.href = "/";
            return false;
        }
    </script>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

从前端输入用户名和密码到后端,js对账号和密码的为空做了验证

controller

controllers/login.go

package controllers

import (
    "github.com/astaxie/beego"
    "github.com/astaxie/beego/context"
)

type LoginController struct {
    beego.Controller
}

func (this *LoginController) Get() {
    // 用于退出
    isExit := this.Input().Get("exit") == "true"
    if isExit {
        this.Ctx.SetCookie("uname", "", -1, "/")
        this.Ctx.SetCookie("pwd", "", -1, "/")
        this.Redirect("/", 301)
    return
    }
    // 用于登录
    this.TplName = "login.html"
}

func (this *LoginController) Post() {
    // 获取数据
    uname := this.Input().Get("uname")
    pwd := this.Input().Get("pwd")
    // 直接进行比对判断
    autoLogin := this.Input().Get("autoLogin") == "on"

    // 认证登录
    if beego.AppConfig.String("uname") == uname && beego.AppConfig.String("pwd") == pwd {
        maxAge := 0
        if autoLogin {
            maxAge = 1<<31 - 1
        }
        this.Ctx.SetCookie("uname", uname, maxAge, "/")
        this.Ctx.SetCookie("pwd", pwd, maxAge, "/")
    }
    this.Redirect("/", 301)
    return
}

// cookie认证
func checkAccount(ctx *context.Context) bool {
    ck, err := ctx.Request.Cookie("uname")
    if err != nil {
        return false
    }
    uname := ck.Value
    //上边已经声明
    ck, err = ctx.Request.Cookie("pwd")
    if err != nil {
        return false
    }
    pwd := ck.Value
    return beego.AppConfig.String("uname") == uname && beego.AppConfig.String("pwd") == pwd
}

首页对登录进行认证

controllers/default.go

package controllers

import (
        "github.com/astaxie/beego"
)

type MainController struct {
        beego.Controller
}

func (this *MainController) Get() {
        this.Data["IsHome"] = true
        this.TplName = "home.html"
        this.Data["IsLogin"] = true// checkAccount(this.Ctx)
}

分类

创建路由

main.go

package main

import (
    _ "beeblog/routers"
        "beeblog/controllers"
        "beeblog/models"
    "github.com/astaxie/beego"
        "github.com/astaxie/beego/orm"
)

func init() {
    models.RegisterDB()
}

func main() {
    orm.Debug = true
    orm.RunSyncdb("default", false, true)
    beego.Router("/", &controllers.MainController{})
    beego.Router("/login", &controllers.LoginController{})
    beego.Router("/category", &controllers.CategoryController{})
    // 自动路由,结构必须以Controller结尾
    beego.AutoRouter(&controllers.TopicController{})
    beego.Router("/reply", &controllers.ReplyController{})
    beego.Run()
}

前端页面

{{template "header1" .}}
 <title>分类 - 我的beego博客</title>
{{template "container" .}}
    <div class="container">
        <h1>分类列表</h1>
        <form method="get" action="/category">
          <div class="form-group">
            <label>分类名称</label>
            <input id="name" name="name" class="form-control" placeholder="Enter Account">
          </div>
          <input type="hidden" name="op" value="add">
          <button type="submit" class="btn btn-default" onclick="return checkInput();">添加</button>
        </form>
        <table class="table table-striped">
            <thead>
                <tr>
                    <th>#</th>
                    <th>名称</th>
                    <th>文章数</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {{range .Categories}}
                <tr>
                    <th>{{.Id}}</th>
                    <th>{{.Title}}</th>
                    <th>{{.TopicCount}}</th>
                    <th>
                        <a href="/category?op=del&id={{.Id}}">删除</a>
                    </th>
                </tr>
                {{end}}
            </tbody>
        </table>
    </div>
    <script type="text/javascript">
        function checkInput() {
            var name = document.getElementById("name")
            if (name.value.length == 0) {
                alert("请分类名称");
                return false
            }
            return true
        }
    </script>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

使用form进行添加,使用/category?op=del&id={{.Id}}进行删除

数据库操作

models/models.go


func AddCategory(name string) error {
    o := orm.NewOrm()
    t := time.Now()

    // 这里传递的一个引用
    cate := &Category{Title: name}
    // 获取表
    qs := o.QueryTable("category")
    // 查找数据是否存在
    err := qs.Filter("title", name).One(cate)
    if err == nil {
       return err
    }

    cate = &Category{Title: name, Created: t, TopicTime: t}
    // 插入数据
    _, err = o.Insert(cate)
    if err != nil {
        return err
    }
    return nil
}

func GetAllCategories() ([]*Category, error) {
    o := orm.NewOrm()

    cates := make([]*Category, 0)
    qs := o.QueryTable("category")
    _, err := qs.All(&cates)
    return cates, err
} 


func DelCategory(id string) error {
    cid, err := strconv.ParseInt(id, 10, 64)
    if err != nil {
        return err
    }
    o := orm.NewOrm()
    cate := &Category{Id: cid}
    _, err = o.Delete(cate)
    return err
}

controller业务逻辑

package controllers

import (
    "github.com/astaxie/beego"
    "beeblog/models"
)

type CategoryController struct {
    beego.Controller
}

func (this *CategoryController) Get() {
    op := this.Input().Get("op")
    switch op {
    case "add":
        name := this.Input().Get("name")
        err := models.AddCategory(name)
        if err == nil {
            beego.Error(err)
        }
        this.Redirect("/category", 301)
        return
    case "del":
        id := this.Input().Get("id")
        err := models.DelCategory(id)
        if err == nil {
            beego.Error(err)
        }
        this.Redirect("/category", 301)
        return
    }

    this.Data["IsCategory"] = true
    this.TplName = "category.html"
    var err error
    this.Data["Categories"],err = models.GetAllCategories()

    if err != nil {
        beego.Error(err)
    }
}

文章添加和展示

路由

main.go

package main

import (
    _ "beeblog/routers"
        "beeblog/controllers"
        "beeblog/models"
    "github.com/astaxie/beego"
        "github.com/astaxie/beego/orm"
)

func init() {
    models.RegisterDB()
}

func main() {
    orm.Debug = true
    orm.RunSyncdb("default", false, true)
    beego.Router("/", &controllers.MainController{})
    beego.Router("/login", &controllers.LoginController{})
    beego.Router("/category", &controllers.CategoryController{})
    beego.Router("/topic", &controllers.TopicController{})
    // 自动路由,结构必须以Controller结尾
    beego.AutoRouter(&controllers.TopicController{})
    beego.Router("/reply", &controllers.ReplyController{})
    beego.Run()
}

前端页面

用于添加文章

views/topic_add.html

{{template "header1" .}}
 <title>添加文章 - 我的beego博客</title>
{{template "container" .}}
    <div class="container">
        <h1>添加文章</h1>
        <form method="post" action="/topic/">
            <div class="form-group">
                <label>文章标题: </label>
                <input type="text" name="title" class="form-control">
            </div>
            <div class="form-group">
                <label>文章内容: </label>
                <textarea class="form-control" name="content" cols="30" rows="10"></textarea>
            </div>
            <button type="submit" class="btn btn-default">添加文章</button>
        </form>
    <script type="text/javascript">
        function checkInput() {
            var title = document.getElementById("title")
            if (title.value.length == 0) {
                alert("请输入文章标题);
                return false
            }
            return true
        }
    </script>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

用于展示文章

{{template "header1" .}}
 <title>文章 - 我的beego博客</title>
{{template "container" .}}
    <div class="container">
        <h1>文章列表</h1>
        <a href="/topic/add" class="btn btn-default">添加文章</a>
        <table class="table table-striped">
            <thead>
                <tr>
                    <th>#</th>
                    <th>名称</th>
                    <th>文章数</th>
                    <th>浏览</th>
                    <th>回复</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {{range .Topics}}
                <tr>
                    <th>{{.Id}}</th>
                    <th>{{.Title}}</th>
                    <th>{{.Content}}</th>
                    <th>{{.Views}}</th>
                    <th>{{.ReplyCount}}</th>
                    <th>
                        <a href="/topic/delete/{{.Id}}">删除</a>
                    </th>
                </tr>
                {{end}}
            </tbody>
        </table>
    </div>
    <script type="text/javascript">
        function checkInput() {
            var name = document.getElementById("name")
            if (name.value.length == 0) {
                alert("请分类名称");
                return false
            }
            return true
        }
    </script>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

数据库操作

models/models.go

func AddTopic(title, content string) error {
    o := orm.NewOrm()

    topic := &Topic{
        Title:     title,
        Content:   content,
        Created:   time.Now(),
        Updated:   time.Now(),  
        ReplyTime: time.Now(),
    }

    _, err := o.Insert(topic)
    return err
}


func GetAllTopics(isDesc bool) ([]*Topic, error) {

    o := orm.NewOrm()
    topics := make([]*Topic, 0)
    qs := o.QueryTable("topic")
    var err error
    // 用于倒叙
    if isDesc {
        // create为小写
        _, err = qs.OrderBy("-created").All(&topics)
    }else{
        _, err = qs.All(&topics)
    }
    return topics, err
}

controller业务逻辑

controllers/topic.go

package controllers

import (
    "github.com/astaxie/beego"
    "beeblog/models"
)

type TopicController struct {
    beego.Controller
}

func (this *TopicController) Get() {
    this.Data["IsLogin"] = checkAccount(this.Ctx)
    this.Data["IsTopic"] = true
    this.TplName = "topic.html"
    var err error
    category := ""
    this.Data["Topics"], err = models.GetAllTopics(category)
    if err != nil {
        beego.Error(err)
    }
}

func (this *TopicController) Post() {
    if !checkAccount(this.Ctx) {
        this.Redirect("/login", 302)
        return
    }
    title := this.Input().Get("title")
    content := this.Input().Get("content")
    tid := this.Input().Get("tid")

    var err error
    if len(tid) != 0 {
        err = models.ModifyTopic(tid, title, content)
    }else{
        err = models.AddTopic(title, content)
    }
    if err != nil {
        beego.Error(err)
    }
    this.Redirect("/topic", 302)
}

func (this *TopicController) Add() {
    //this.Ctx.WriteString("add")
    this.TplName = "topic_add.html"
}

首页获取文章

views/home.html

{{template "header1" .}}
 <title>首页 - 我的beego博客</title>
{{template "container" .}}
    <div class="container theme-showcase">
        {{range .Topics}}
        <div class="page-header">
            <h1>{{.Title}}</h1>
            <h6 class="text-muted">文章发表{{.Create}}, 共有{{.Views}}次浏览,{{.ReplyCount}}个评论.</h6>
            <p>
                {{.Content}}
            </p>
        </div>
        {{end}}
    </div>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

controllers/default.go

package controllers

import (
    "github.com/astaxie/beego"
    "beeblog/models"
)

type MainController struct {
        beego.Controller
}

func (this *MainController) Get() {
    this.Data["IsHome"] = true
    this.TplName = "home.html"
    this.Data["IsLogin"] = checkAccount(this.Ctx)
    this.Data["Categories"], err := models.GetAllCategories()
    if err != nil {
        beego.Error(err)
    }

}

查看文章,修改和删除

controller

controllers/topic.go

func (this *TopicController) View() {
   this.TplName = "topic_view.html" 
   // 获取自动路由的参数
   tidMap := this.Ctx.Input.Params()
   this.Data["Tid"] = tidMap["0"]
   topic, err := models.GetTopic(tidMap["0"])
   if err != nil {
       beego.Error(err)
       this.Redirect("/", 302)
       return
   }

   this.Data["Topic"] = topic

   replies, err := models.GetAllReplies(tidMap["0"])
   if err != nil {
       beego.Error(err)
       return
   }
   this.Data["Replies"] = replies
   this.Data["IsLogin"] = checkAccount(this.Ctx)
}


func (this *TopicController) Modify() {
    this.TplName = "topic_modify.html"

    tid := this.Input().Get("tid")
    topic, err := models.GetTopic(tid)
    if err != nil {
        beego.Error(err)
    }
    this.Data["Topic"] = topic
    this.Data["Tid"] = tid
}


func (this *TopicController) Delete() {
    if !checkAccount(this.Ctx) {
        this.Redirect("/login", 302)
        return
    }
    tidMap := this.Ctx.Input.Params()
    tid := tidMap["0"]
    err := models.DeleteTopic(tid)
    if err != nil {
        beego.Error(err)
    }
    this.Redirect("/topic", 302)
}

前端界面

查看

views/topic_view.html

{{template "header1" .}}
 <title>{{.Topic.Title}} - 我的beego博客</title>
{{template "container" .}}
    <div class="container">
        <h1>{{.Topic.Title}}</h1>
        <a href="/topic/modify?tid={{.Tid}}" class="btn btn-default">修改文章</a>
        {{.Topic.Content}}
    </div>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

编辑

views/topic_modify.html

{{template "header1" .}}
 <title>添加文章 - 我的beego博客</title>
{{template "container" .}}
    <div class="container">
        <h1>添加文章</h1>
        <form method="post" action="/topic">
            <input class="hidden" type="text" name="tid" value="{{.Topic.Id}}">
            <div class="form-group">
                <label>文章标题: </label>
                <input type="text" name="title" class="form-control" value="{{.Topic.Title}}">
            </div>
            <div class="form-group">
                <label>文章内容: </label>
                <textarea class="form-control" name="content" cols="30" rows="10">{{.Topic.Content}}</textarea>
            </div>
            <button type="submit" class="btn btn-default">添加文章</button>
        </form>
    <script type="text/javascript">
        function checkInput() {
            var name = document.getElementById("name")
            if (name.value.length == 0) {
                alert("请分类名称");
                return false
            }
            return true
        }
    </script>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

数据库操作

orm这边可以定义一个全局的

func GetTopic(tid string) (*Topic, error) {
    tidNum, err := strconv.ParseInt(tid, 10, 64)
    if err != nil {
       return nil, err
    }

    o := orm.NewOrm()
    topic := new(Topic)   
    qs := o.QueryTable("topic")
    err = qs.Filter("id", tidNum).One(topic)
    if err != nil{
        return nil, err
    }
    topic.Views++
    _, err = o.Update(topic)

    return topic, err
}


func ModifyTopic(tid, title, content string) error {
    // 转为int64
    tidNum, err := strconv.ParseInt(tid, 10, 64)
    if err != nil {
       return err
    }

    o := orm.NewOrm()
    topic := &Topic{Id: tidNum}

    if o.Read(topic) == nil {
        topic.Title = title
        topic.Content = content
        topic.Updated = time.Now()
        o.Update(topic)
    }
    return err
}


func DeleteTopic(tid string) error {
    tidNum, err := strconv.ParseInt(tid, 10, 64)
    if err != nil {
       return err
    }
    o := orm.NewOrm()
    topic := &Topic{Id: tidNum}
    _, err = o.Delete(topic)
    return err
}

文章设置分类

前端界面

add添加增加的地方,views/topuc_add.html

{{template "header1" .}}
 <title>添加文章 - 我的beego博客</title>
{{template "container" .}}
    <div class="container">
        <h1>添加文章</h1>
        <form method="post" action="/topic/">
            <div class="form-group">
                <label>文章标题: </label>
                <input type="text" name="title" class="form-control">
            </div>
            <div class="form-group">
                <label>文章分类: </label>
                <input type="text" name="category" class="form-control">
            </div>
            <div class="form-group">
                <label>文章内容: </label>
                <textarea class="form-control" name="content" cols="30" rows="10"></textarea>
            </div>
            <button type="submit" class="btn btn-default">添加文章</button>
        </form>
    <script type="text/javascript">
        function checkInput() {
            var title = document.getElementById("title")
            if (title.value.length == 0) {
                alert("请输入文章标题);
                return false
            }
            return true
        }
    </script>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

motify添加原来的值,views/topic_modify.html

{{template "header1" .}}
 <title>添加文章 - 我的beego博客</title>
{{template "container" .}}
    <div class="container">
        <h1>添加文章</h1>
        <form method="post" action="/topic">
            <input class="hidden" type="text" name="tid" value="{{.Topic.Id}}">
            <div class="form-group">
                <label>文章标题: </label>
                <input type="text" name="title" class="form-control" value="{{.Topic.Title}}">
            </div>
            <div class="form-group">
                <label>文章分类: </label>
                <input type="text" name="title" class="form-control" value="{{.Topic.Category}}">
            </div>
            <div class="form-group">
                <label>文章内容: </label>
                <textarea class="form-control" name="content" cols="30" rows="10">{{.Topic.Content}}</textarea>
            </div>
            <button type="submit" class="btn btn-default">添加文章</button>
        </form>
    <script type="text/javascript">
        function checkInput() {
            var name = document.getElementById("name")
            if (name.value.length == 0) {
                alert("请分类名称");
                return false
            }
            return true
        }
    </script>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>

controller

POST方法添加参数

controllers/topic.go

func (this *TopicController) Post() {
    if !checkAccount(this.Ctx) {
        this.Redirect("/login", 302)
        return
    }
    title := this.Input().Get("title")
    content := this.Input().Get("content")
    category := this.Input().Get("category")
    tid := this.Input().Get("tid")

    var err error
    if len(tid) != 0 {
        err = models.ModifyTopic(tid, title, category,  content)
    }else{
        err = models.AddTopic(title, category, content)
    }
    if err != nil {
        beego.Error(err)
    }
    this.Redirect("/topic", 302)
}

数据库处理

model的add和motify也需要加一下

type Topic struct {
    Id                int64
    Uid               int64
    Title             string
    Category          string
    Content           string `orm: "size(5000)"`
    Attachment        string
    Created           time.Time `orm: "index"`
    Updated           time.Time `orm: "index"`
    Views             int64
    Author            string
    ReplyTime         time.Time `orm: "index"`
    ReplyCount        int64
    ReplyLastUserId   int64
}

...

func AddTopic(title, category, content string) error {
    o := orm.NewOrm()

    topic := &Topic{
        Title:     title,
        Category:  category,
        Content:   content,
        Created:   time.Now(),
        Updated:   time.Now(),
        ReplyTime: time.Now(),
    }

    _, err := o.Insert(topic)
    return err
}

...

func ModifyTopic(tid, title, category , content string) error {
    // 转为int64
    tidNum, err := strconv.ParseInt(tid, 10, 64)
    if err != nil {
       return err
    }

    o := orm.NewOrm()
    topic := &Topic{Id: tidNum}

    if o.Read(topic) == nil {
        topic.Title = title
        topic.Category = category
        topic.Content = content
        topic.Updated = time.Now()
        o.Update(topic)
    }
    return err
}

添加评论

前端页面

views/topic_view.html

{{template "header1" .}}
 <title>{{.Topic.Title}} - 我的beego博客</title>
{{template "container" .}}
    <div class="container">
        <h1>{{.Topic.Title}}</h1>
        <a href="/topic/modify?tid={{.Tid}}" class="btn btn-default">修改文章</a>
        {{.Topic.Content}}
    </div>
    <div class="container">
        <h3>文本回复</h3>
        <form method="post" action="/reply/add">
            <input type="hidden" name="tid" value="{{.Topic.Id}}">
            <div class="form-group">
                <label>显示昵称:</label>
                <input type="text" class="form-control" name="nickname">
            </div>
            <div class="form-group">
                <label>内容:</label>
                <textarea type="content" id="" cols="30" rows="10" class="form-control"></textarea>
            </div>
            <button class="btn btn-default">回复</button>
        </form>
    </div>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

controller

controllers/reply.go

package controllers

import (
    "github.com/astaxie/beego"
    "beeblog/models"
)

type ReplyController struct {
    beego.Controller
}


func (this *ReplyController) Add() {
    tid := this.Input().Get("tid")
    nickname := this.Input().Get("nickname")
    content := this.Input().Get("content")
    err := models.AddReply(tid, nickname, content)
    if err != nil {
        beego.Error(err)
    }
    this.Redirect("/topic/view/"+tid, 302)
}

数据库操作

创建model

models/models.go

type Comment struct {
    Id                int64
    Tid               int64
    Name              string
    Content           string `orm: "size(1000)"`
    Created           time.Time `orm: "index"`
}

对于回复的操作

func AddReply(tid, nickname, content string) error {
    tidNum, err := strconv.ParseInt(tid, 10, 64)
    if err != nil {
       return err
    }
    reply := &Comment{
        Tid: tidNum,
        Name: nickname,
        Content: content,
        Created: time.Now(),
    }
    o := orm.NewOrm()
    _, err = o.Insert(reply)
    return err
}

设置路由

main.go

package main

import (
    _ "beeblog/routers"
        "beeblog/controllers"
        "beeblog/models"
    "github.com/astaxie/beego"
        "github.com/astaxie/beego/orm"
)

func init() {
    models.RegisterDB()
}

func main() {
    orm.Debug = true
    orm.RunSyncdb("default", false, true)
    beego.Router("/", &controllers.MainController{})
    beego.Router("/login", &controllers.LoginController{})
    beego.Router("/category", &controllers.CategoryController{})
    beego.Router("/topic", &controllers.TopicController{})
    // 自动路由,结构必须以Controller结尾
    beego.AutoRouter(&controllers.TopicController{})
    beego.Router("/reply", &controllers.ReplyController{})
    beego.Router("/reply/add", &controllers.ReplyController{}, "post:Add")
    beego.Run()
}

显示回复

前端显示

views/topic_view.html

{{template "header1" .}}
 <title>{{.Topic.Title}} - 我的beego博客</title>
{{template "container" .}}
    <div class="container">
        <h1>{{.Topic.Title}}</h1>
        <a href="/topic/modify?tid={{.Tid}}" class="btn btn-default">修改文章</a>
        {{.Topic.Content}}
    </div>
    <div class="container">
        {{range .Replies}}
        <h3>{{.Name}} <small>{{.Created}}</small>  </h3>
        {{.Content}}
        {{end}}
        <h3>文本回复</h3>
        <form method="post" action="/reply/add">
            <input type="hidden" name="tid" value="{{.Topic.Id}}">
            <div class="form-group">
                <label>显示昵称:</label>
                <input type="text" class="form-control" name="nickname">
            </div>
            <div class="form-group">
                <label>内容:</label>
                <textarea type="content" id="" cols="30" rows="10" class="form-control"></textarea>
            </div>
            <button class="btn btn-default">回复</button>
        </form>
    </div>
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

controller

在views中显示

controllers/topic.go

func (this *TopicController) View() {
   this.TplName = "topic_view.html"
   // 获取自动路由的参数
   tidMap := this.Ctx.Input.Params()
   this.Data["Tid"] = tidMap["0"]
   topic, err := models.GetTopic(tidMap["0"])
   if err != nil {
       beego.Error(err)
       this.Redirect("/", 302)
       return
   }

   this.Data["Topic"] = topic

   replies, err := models.GetAllReplies(tidMap["0"])
   if err != nil {
       beego.Error(err)
       return
   }
   this.Data["Replies"] = replies
   this.Data["IsLogin"] = checkAccount(this.Ctx)
}

数据库操作

func GetAllReplies(tid string) (replies []*Comment, err error) {
    tidNum, err := strconv.ParseInt(tid, 10, 64)
    if err != nil {
       return nil, err
    }
    replies = make([]*Comment, 0)
    o := orm.NewOrm()
    qs := o.QueryTable("comment")
    _, err = qs.Filter("tid", tidNum).All(&replies)
    return replies, err
}

删除评论

前端只需要提交一个删除操作

        {{$tid := .Topic.Id}}
        {{$islogin := .IsLogin}}
        {{range .Replies}}
        <h3>{{.Name}} <small>{{.Created}}</small>  {{if $islogin}}</h3><a href="/reply/delete?tid={{$tid}}&rid={{.Id}}">删除</a>{{end}}
        {{.Content}}
        {{end}}

注册路由

beego.Router("/reply/delete", &controllers.ReplyController{}, "get:Delete")

controller对应的方法

func (this *ReplyController) Delete() {
    if !checkAccount(this.Ctx) {
        return
    }
    tid := this.Input().Get("tid")
    rid := this.Input().Get("rid")
    err := models.DeleteReply(rid)
    if err != nil {
        beego.Error(err)
    }
    this.Redirect("/topic/view/"+tid, 302)
}

数据库对应的操作

func DeleteReply(rid string) error {
    ridNum, err := strconv.ParseInt(rid, 10, 64)
    if err != nil {
       return err
    }
    o := orm.NewOrm()
    reply := &Comment{Id: ridNum}
    _, err = o.Delete(reply)
    return err
}

分类显示

views/home.html

{{template "header1" .}}
 <title>首页 - 我的beego博客</title>
{{template "container" .}}
    <div class="container theme-showcase">
        <div class="col-md-9">
        {{range .Topics}}
            <div class="page-header">
                <h1>{{.Title}}</h1>
                <h6 class="text-muted">文章发表{{.Created}}, 共有{{.Views}}次浏览,{{.ReplyCount}}个评论.</h6>
                <p>
                    {{.Content}}
                </p>
            </div>
        </div>
        {{end}}
    </div>
    <div class="col-md-3">
        <h3>文章分类</h3>
        <ul>
            {{range .Categories}}
            <li><a href="/?cate={{.Title}}">{{.Title}}</a></li>
            {{end}}
    <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

controllers/default.go

package controllers

import (
    "github.com/astaxie/beego"
    "beeblog/models"
)

type MainController struct {
    beego.Controller
}

func (this *MainController) Get() {
    var err error
    this.Data["IsHome"] = true
    this.TplName = "home.html"
    this.Data["IsLogin"] = checkAccount(this.Ctx)
    this.Data["Topics"], err = models.GetAllTopics(this.Input().Get("cate"), true)
    if err != nil {
        beego.Error(err)
    }

    categories, err := models.GetAllCategories()
    if err != nil{
        beego.Error(err)
    }
    this.Data["Categories"] = categories
}

models/models.py

func GetAllTopics(cate string, isDesc bool) ([]*Topic, error) {

    o := orm.NewOrm()
    topics := make([]*Topic, 0)
    qs := o.QueryTable("topic")
    var err error
    // 用于倒叙
    if isDesc {
        if len(cate) > 0 {
            qs = qs.Filter("category", cate)
        }
        // create为小写
        _, err = qs.OrderBy("-created").All(&topics)
    }else{
        _, err = qs.All(&topics)
    }
    return topics, err
}

将原来调用GetAllTopics方法的第一个参数改为空字符串