Beego入门(一) - Go语言中文社区

Beego入门(一)


Beego案例-新闻发布系统

1.注册

后台代码和昨天案例代码一致。,所以这里面只写一个注册的业务流程图。

**业务流程图 **

1538500613581.png

2.登陆

业务流程图

1538500659872.png

登陆和注册业务和我们昨天登陆和注册基本一样,所以就不再重复写这个代码

但是我们遇到的问题是如何做代码的迁移,把昨天的登陆和注册拿过来直接用?

  • 首先,我们需要把静态页面拷贝到我们项目目录下面。

    • 进入项目目录,删除掉原来的static文件夹
1538591447624.png
  • 然后拷贝我们昨天课堂资料中的 static.zip到这个目录,并解压,解压之后如下图:
1538591575762.png
  • 打开static文件夹,显示如下,则说明拷贝成功:
1538591636455.png
  • 把static文件夹中所有的html文件都拷贝到views文件夹下面(昨天的几个页面已经没用了,可以删除),拷贝之后views文件显示如下:
1538591824871.png

这时候用GoLand打开我们的项目,显示如下:

1538591946260.png
  • 打开register.html页面,修改页面中form表单的内容

    • <form>标签加上action="/register" method="post"属性

    • 给两个<input>标签的name分别改为name="userName"name = "passwd"

    • <form>表单相关代码如下:

      <form  class="login_form" name = "login" action="/register" method="post">
           <h1 class="login_title">用户注册</h1>
           <input type="text" placeholder="用户名" class="input_txt" name="userName">
           <input type="password" placeholder="密码" class="input_txt" name = "passwd">
           <input type="submit" value="注 册" class="input_sub">
      </form>
      
  • 打开login.html页面,修改form表单的内容

    • <form>标签加上action="/login" method="post"属性

    • 给两个<input>标签的name分别改为name="userName"name = "passwd"

    • <form>表单相关代码如下:

      <form  class="login_form"  name = "login" action="/login" method="post">
          <h1 class="login_title">用户登录</h1>
          <input type="text"  class="input_txt" name = "userName">
          <input type="password" name = "passwd"  class="input_txt">
          <div class="remember"><input type="checkbox" name="remember" >
              <label>记住用户名</label>
          </div>
          <input type="submit" value="登 录" class="input_sub">
      </form>
      

      (登陆界面多了个记住用户名标签,明天我们实现这个功能)

改完之后,运行项目,测试注册和登陆页面能够能唱显示,并且功能没有问题说明代码迁移成功。

3.创建数据库

3.1数据库表的设计

接下来我们就要实现文章相关的操作,所以这里我们要在数据库中生成一个文章表。

我们以前在数据库中创建表的时候,会给字段加很多限制属性,比如非空,长度,默认值等等,在ORM中,创建表时也可以给各个字段添加相应的限制。那如何去加限制呢?我们先看例子:

type Article struct {
    Id int `orm:"pk;auto"`
    ArtiName string `orm:"size(20)"`
    Atime time.Time `orm:"auto_now"`
    Acount int `orm:"default(0);null"`
    Acontent string `orm:"size(500)"`
    Aimg string  `orm:"size(100)"`
}

由上面的代码可以看出,要给哪个字段添加属性,需要在这个字段后面添加 `` 括起来的内容,格式为orm:"限制条件"。那这些限制条件都有哪些呢?我在这里给大家列了一个表格。

限制条件 作用
pk 设置该字段为主键
auto 这只该字段自增,但是要求该字段必须为整型
default(0) 设置该字段的默认值,需要注意字段类型和默认值类型一致
size(100) 设置该字段长度为100个字节,一般用来设置字符串类型
null 设置该字段允许为空,默认不允许为空
unique 设置该字段全局唯一
digits(12);decimals(4) 设置浮点数位数和精度。比如这个是说,浮点数总共12位,小数位为四位。
auto_now 针对时间类型字段,作用是保存数据的更新时间
auto_now_add 针对时间类型字段,作用是保存数据的添加时间

注意:当模型定义里没有主键时,符合int, int32, int64, uint, uint32, uint64 类型且名称为 Id 的 Field 将被视为主键,能够自增. "

Mysql中时间类型有date和datetime两种类型,但是我们go里面只有time.time一种类型,如果项目里面要求精确的话,就需要指定类型,指定类型用的是type(date)或者type(datetime)

3.2生成表

这时候注意,我们添加了结构体对象之后,并不能直接生成表,需要注册,注册的代码就是初始化数据库三行代码中的第二行,注册表结构,把要创建的表对应的结构体对象作为函数的参数,代码如下:

orm.RegisterModel(new(User),new(Article))

创建之后,我们可以在goland下方查看创建表过程,也可以进入数据库查看是否建表成功,成功的话,数据库显示如下:

1538619910805.png

登陆成功之后,访问新闻列表展示页面,但是我们现在还没有新闻,所以我们先实现插入文章界面。

4.插入文章

业务流程图


1538595086914.png

插入页面我们用的视图是add.html,这里我们规定添加文章界面的请求路径为/addArticle

4.1修改路由文件

在router.go文件的init函数中添加下面这一行代码

beego.Router("/addArticle",&controllers.ArticleControlle{},"get:ShowAddArticle")

4.2添加文章界面的显示

  • 先创建一个article.go文件用来存放文章有关的业务代码

  • 然后在article.go文件中创建一个ArticleController控制器,并定义一个ShowAddArticle函数代码如下:

    import "github.com/astaxie/beego"
    
    type ArticleController struct {
      beego.Controller
    }
    
    func (this*ArticleController)ShowAddArticle(){
    
    }
    
  • 接着我们来实现ShowAddArticle函数,这个函数只是用来展示页面的,所以我们只需要给他制定一个视图就可以,代码如下:

    func (this*ArticleController)ShowAddArticle(){
      this.TplName = "add.html"
    }
    

    写完代码之后,我们从浏览器发出一个请求http://192.168.110.71:8080/addArticle,如果能在浏览器中看到下面这个界面,表示页面展示成功:

1538620374676.png

4.3插入文章数据处理

上面我们显示了添加文章界面,观察界面可以发现,我们需要获取文章标题文章类型, 文章内容上传图片。其中文章类型牵涉到多表操作,我们放到明天来讲,今天只讲简单的单表操作。首先让我们来看一下,插入页面的前端部分修改。

4.3.1前端页面修改

由页面可知,我们这里面是要上传数据,所以我们这里需要一个form表单,打开前端界面add.html,能看到我们这里面确实有一个<form>标签,只是没有属性,我们需要给<form>标签添加action和method属性,这个请求还是添加文章,所以我们还可以用添加文章的请求路径,设置action属性action="/addArticle"。因为上传数据,我们这里用post方法,设置method属性method="post"。其他部分不用修改。form修改代码如下:

 <form method="post" action="/addArticle">

4.3.2路由内容修改

我们在前端添加了addArticle请求的post方法,所以需要修改一下router.go,给addArticle的post请求指定一个函数,修改代码如下:

beego.Router("/addArticle",&controllers.ArticleController{},"get:ShowAddArticle;post:HandleAddArticle")

4.3.3后台代码实现

有了函数名之后,我们就需要在后台中实现这个函数。

  • 首先是获取数据

    这时候我们看一下前端界面,我们需要获取文章标题, 文章内容上传图片数据,文章标题和文章内容都是字符串,比较简单,直接通过GetString获取,所以我们先获取这两个内容。通过查看add.html代码我们发现,文章标题对应的input标签name等于articleName,文章内容对应的textarea标签name等于content(注意这里用的是textarea标签,不是用的input,但是获取数据方式一样)。获取数据的代码如下:

    //获取数据
    articleName := this.GetString("articleName")
    content := this.GetString("content")
    
  • 获取数据之后就做数据校验,我们这里还是做判空校验

    //对数据进行校验
    if articleName == ""|| content == ""{
      beego.Info("添加文章数据不完整")
      this.TplName = "add.html"
      return 
    }
    

    正常的添加流程,在校验完数据之后就要把数据插入数据库了,但是我们添加文章这个界面有点特殊,因为这里面牵涉到一个静态文件的上传,所以我们先处理静态文件上传功能。

  • 静态文件上传(难点)

    • 前端代码

      如果form表单中牵涉到文件上传,在form表单中就需要添加一个属性enctype="multipart/form-data"不然上传就是假上传,后台不能获取到上传的文件。<form>修改如下:

      <form method="post" action="/addArticle" enctype="multipart/form-data">
          <input type="file" class="input_file"  name="uploadname">
      
    • 后台代码修改

      后台接收上传文件有两个函数可以用。

      **GetFile(key string) (multipart.File, *multipart.FileHeader, error) **

      **作用 **是获取前端传递过来的文件。

      参数 是input标签中的name值

      返回值 有三个,一个是文件流(就是我们打开文件返回的内容),第二个是文件相关信息,包括文件头,文件大小,文件名字等,第三个是错误信息。示例代码如下:

          file,head,err := this.GetFile("uploadname")
          if err != nil{
              beego.Info("上传图片错误,请重新添加!")
              this.TplName = "add.html"
              return
          }
      

      **SaveToFile(fromfile, tofile string) error **

      作用直接保存前端出过来的文件。

      参数 有两个参数,第一个参数是前端input标签的name属性值,第二个参数是文件在服务器端存储的位置。注意:这个位置字符串在前面需要加一个.

      返回值是错误信息。示例代码如下:

      err := this.SaveToFile("uploadname","./static/img/1.jpg")
      if err != nil{
              beego.Info("上传图片错误,请重新添加!")
              this.TplName = "add.html"
              return
          }
      
    • 在我们开发过程中,如果后台接收文件并存储需要做以下几种判断

      文件格式判断

      我们通过GetFile可以获取到文件名,然后通过path包,可以分离出文件的后缀,即文件格式,把你需要的文件格式过滤出来,不需要的返回即可。我们根据文件名获取文件后缀,代码如下:

      //文件格式判断
          fileExt := path.Ext(head.Filename)
          if fileExt != ".jpg" && fileExt != ".png" && fileExt != ".jpeg"{
              beego.Info("上传图片格式不正确,请重新添加!")
              this.TplName = "add.html"
              return
          }
      

      文件大小的判断

      我们获取文件之后,在存储之前,文件流一般是在内存中,所以文件不易过大,我们在这里做一个文件大小的判断。代码如下:

      //文件大小判断
          if head.Size > 5000000{
              beego.Info("上传图片太大,请重新添加!")
              this.TplName = "add.html"
              return
          }
      

      避免文件重名

      获取文件之后我们要把文件存储到服务器上,但是用户可能会上传同名的文件,如果文件同名的话,后来上传的文件就把之前上传的文件给覆盖了,所以我们要给上传的文件重新确定一个名字。这里我们以上传文件时的时间作为上传文件的文件名。默认的时间格式和我们常见的时间格式不一样,所以这里我们需要对事件做一个格式化。格式化字符串为"2006-01-02-15-04-05(规定的必须是这个,方便记忆可以用6-1-2-3-4-5来记)代码如下:

      fileName := time.Now().Format("2006-01-02-15-04-05")
      //存储
      this.SaveToFile("uploadname","./static/img/"+fileName+fileExt)
      
  • 保存数据到数据库

    这里是数据的插入操作,我们不做详细解释,直接看代码:

      //插入数据库
      //获取orm对象
      o := orm.NewOrm()
      //获取要插入的对象
      var article models.Article
      //给对象赋值
      article.ArtiName = articleName
      article.Acontent = content
      //这一步需要注意,我们存储的图片是图片地址,没有 .
      article.Aimg = "/static/img/"+fileName+fileExt
      //插入
      o.Insert(&article)
    
  • 返回视图

    如果没有视图,先返回一句话,代码如下:

    this.Ctx.WriteString("添加成功")
    
  • 完整代码如下:

    //获取数据
      articleName := this.GetString("articleName")
      content := this.GetString("content")
    
    //数据校验
      if articleName == "" || content == ""{
          beego.Info("添加文章数据不完整,请重新输入")
          this.TplName = "add.html"
          return
      }
    
    //获取上传图片
      file,head,err := this.GetFile("uploadname")
      defer file.Close()
    
      if err != nil{
          beego.Info("上传图片错误,请重新添加!")
          this.TplName = "add.html"
          return
      }
      //文件格式判断
      fileExt := path.Ext(head.Filename)
      if fileExt != ".jpg" && fileExt != ".png" && fileExt != ".jpeg"{
          beego.Info("上传图片格式不正确,请重新添加!")
          this.TplName = "add.html"
          return
      }
    
      //文件大小判断
      if head.Size > 5000000{
          beego.Info("上传图片太大,请重新添加!")
          this.TplName = "add.html"
          return
      }
    
      //避免文件重名
      fileName := time.Now().Format("2006-01-02-15-04-05")
      this.SaveToFile("uploadname","./static/img/"+fileName+fileExt)
    
    //插入数据库
      //获取orm对象
      o := orm.NewOrm()
      //获取要插入的对象
      var article models.Article
      //给对象赋值
      article.ArtiName = articleName
      article.Acontent = content
      //这一步需要注意,我们存储的图片是图片地址,没有.
      article.Aimg = "/static/img/"+fileName+fileExt
      //插入
      o.Insert(&article)
    
    //返回视图
      this.Ctx.WriteString("添加成功")
    

5.新闻信息展示

添加文章之后我们回到文章显示界面,我们这里固定显示文章列表页的请求为/ShowArticleList,然后给这个请求指定控制器,以及相应的方法修改。

5.1修改路由文件

首先我们修改路由文件,代码如下:

beego.Router("/ShowArticleList",&controllers.ArticleController{},"get:ShowArticleList")

5.2后台代码

修改路由文件之后,我们实现ShowArticleList函数。

5.2.1获取所有文章

  • 获取orm对象

    o := orm.NewOrm()
    
  • 定义一个对象数组,用来存储获取的所有对象

    var articles []models.Article
    
  • 指定要查询的数据库表,用QueryTable函数,参数是表名,返回值是queryseter,ORM 以 QuerySeter 来组织查询,每个返回 QuerySeter 的方法都会获得一个新的 QuerySeter 对象。

    qs := o.QueryTable("Article")
    
  • 获取所有数据,用all方法,参数是对象数组地址

    qs.All(&articles)
    
  • 获取数据之后把数据传递给视图,并且指定视图文件

    this.Data["articles"] = articles
    this.TplName = "index.html"
    

    在浏览器里面输入地址之后能获取下面页面,表示代码没有问题

1538669662236.png
  • ORM高级查询(重点)

    我们在后面项目开发中对数据库的查询,一般都是指定数据库表,用高级查询的方法进行查询。ORM支持如下几种高级查询。

    函数名 作用 用法
    Limit() 获取部分数据 有两个参数,第一个参数是指定获取几条数据,第二个参数指定从哪里获取qs.Limit(size,start)。返回值还是qs
    OrderBy() 根据指定的字段排序 只有一个参数,参数作用是指定按照哪个字段排序,返回值是qs
    Distinct() 去重 没有参数,返回值是qs
    Count() 查询符合条件的数据条目数 没有参数,返回值是查询到的条目数和错误信息
    All() 把查询到的数据全部存储到指定的容器里面 只有一个参数,指定存储查询对象的存储容器
    RelatedSel() 多表查询的时候使用,指定关联的数据库表 参数长度不限,关联几个表,放几个参数
    Filter() 过滤器,相当于SQL语句中的where 有两个参数,第一个参数是指定查询条件,第二个参数是值
    ... ... ...

    还有其他很多高级查询,具体参考:https://beego.me/docs/mvc/model/query.md页面查看

5.3前端代码

5.3.1视图循环语法

后台传递给视图的数据是对象数组,要访问到每一个对象需要循环访问这个数组,那我们来看一下这个循环语法。循环语法有两种,一种格式如下:

{{range $index,$val := .articles}}
        {{$val}}        
{{end}}

$index表示的是下标,$val表示的数组元素,循环的内容放在range和end之间。

另外一种循环如下:

{{range .articles}}
    {{.Name}}
{{end}}

在range和end之间通过{{.}}直接获取数组元素的字段值。

5.3.2视图数据展示

了解了视图的循环语法之后,我们就可以循环获取控制器传递过来的对象数组数据。代码如下:

{{range .articles}}
    <tr>
       <td>{{.ArtiName}}</td>
       <td><a href="#">查看详情</a></td>
       <td> {{.Atime.Format "2006-01-02-15-04-05"}}</td>
       <td>{{.Acount}}</td>
       <td><a href="#" class="dels">删除</a></td>
       <td><a href="#">编辑</a></td>
       <td>财经新闻</td>
    </tr>
{{end}}

实现之后我们就可以把添加文章最后的跳转改成显示文章列表页,超链接和文章类型,我们在接下来的页面实现。

5.4数据的分页显示(难点)

观察我们的列表页可以发现,我们文章里表下面是分页展示,接着我们来实现这个分页。分页功能在我们平常浏览网页的时候也比较常见,这里我们先实现简单的分页功能,等到我们项目实战的时候会给大家封装一个分页函数,实现一个高级点的分页。首页显示如下:

1538669662236.png

分页的好处:如果没有分页,我们访问完数据就要全部在页面显示,有分页之后我们可以显示部分数据,好处有一下两点。

  • 方便浏览,分页浏览可以更方便我们平常访问网页。
  • 提高访问网站速度。如果一次性把数据全部从数据库中取出来,效率没有一次取出部分数据块。

了解了上面的内容之后我们开始写代码实现分页的功能,一般开发中遇见这种大的功能模块,我们都是划分为几个小块,一点一点来实现。我们从简单到复杂来实现相应功能,首先我们先获取总页数和总记录数。

5.4.1获取总记录数和总页数

首页显示代码是ShowArticleList函数,所以我们分页的业务代码也在这个函数里面。

  • 获取总记录数,orm是用count函数来获取数据的记录数,没有参数,返回值为记录数和错误信息,代码如下:

    count,err := qs.Count()
    
  • 获取总页数

    总页数 = 总记录数 / 每页显示的数据条数

    总记录数我们已经获取了,所以需要我们自己设置每页显示多少条数据,然后相除就可以获得,代码如下:

    //确定每页显示数
      pageSize := 2
    //获取总页数
      pageCount := count / pageSize
    
  • 把数据传递给视图,并在视图中显示。

    this.Data["count"] = count
    this.Data["pageCount"] = pageCount
    

    这时候你会发现,当你的最后一页显示的数据不满的话,总页数会少计算一页,原因是我们求总页数的计算是两个整数相除,除不尽的时候会自动舍去小数位。这和我们真实的业务不相符。所以我们需要修改获取总页数的代码。怎么修改呢?完全改成浮点数显然也不行,因为总页码不会是小数。这里面我们用天花板函数Ceil()。Ceil()的作用是传递过来一个浮点数,获取比这个浮点数大的又离这个浮点数最近的整数,代码如下:

    //获取总页数
      pageCount :=math.Ceil(float64(count) / float64(pageSize))
    

    页码这时候显示正确。

5.4.2获取首页和末页数据

获取完总页数和总记录数之后,最简单的功能模块就是首页和末页内容的显示。首页和末页,我们需要把相应的页码传递过来才能知道获取哪些数据。那视图如何给后台传递数据呢?我们在平常浏览网页的时候经常会遇到类似于这样的URL地址

http://tieba.baidu.com/f?fr=index&fp=0&ie=utf-8&red_tag=m2329796506

我们重点关注?后面的内容,他们是成对出现的,每对之间用&连接,这种是URL传值的一种。我们在后台通过GetString函数可以获取到相应的值。

  • 设置首页的超链接

    我们可以通过URL传值的方式把页码传递过来。这里我们设置首页的<a>标签超链接为/ShowArticleList?pageIndex=1

  • 获取首页数据

    我们先通过GetString()获取到页码,然后通过页码获取相应的数据。这里给大家介绍数据库获取部分数据的函数**Limit() **

    Limit()

    作用:获取数据库中部分数据

    参数:第一个参数是获取多少数据,第二个参数是从哪里开始取数据

    返回值是queryseter类型,示例代码如下

    qs := qs.Limit(pageSize,start)
    

    我们掌握了limit函数之后,现在要获取数据库中部分数据,pageSize我们已经知道了,这个start怎么去求呢?我们可以根据start的规律来找,比如说,第一页数据的起始位置是0,第二页的其实位置是2,第三页的其实位置是4,你发现起始位置刚好是页码减一乘以pageSize,由此我们得出公式。start = (pageIndex - 1) * pageSize

    那么我们获取首页的代码如下:

    //获取页码
      pageIndex,_ := this.GetInt("pageIndex")
    //确定数据的起始位置
      start := (pageIndex - 1) * pageSize
    //查询数据库部分数据
      qs.Limit(pageSize,start).All(&articles)
    

    这时候有个问题,我们从其他页面跳转到首页的时候没有指定pageIndex,所以我们需要对获取不到pageIndex的情况进行处理,处理方案:当没有获取到pageIndex的时候默认pageIndex等于1,即默认访问首页内容。修改后的代码如下:

    //获取页码
      pageIndex,err := this.GetInt("pageIndex")
      if err != nil{
          pageIndex = 1
      }
    //确定数据的起始位置
      start := (pageIndex - 1) * pageSize
    //查询数据库部分数据
      qs.Limit(pageSize,start).All(&articles)
    
  • 获取末页数据只要参考着首页,把传过来的pageIndex改为总页码数即可。设置末页的链接为/ShowArticleList?pageIndex={{.pageCount}}

    这时候记得把页码也传递给视图

5.4.2获取上一页和下一页数据

前面我们已经获取了首页和末页的数据,仿照着链接,我们可以把上一页下一页的链接也实现,设置上一页的超链接为/ShowArticleList?pageIndex={{.pageIndex}} - 1,但是你在index.html写了这个之后,编辑器会报错,html标签属性不能直接进行数学运算。这时候我们就要想办法,不在视图里面操作,并且给pageIndex减1,方法有很多,这里呢,老师给你们介绍一种beego处理这种简单业务逻辑的方法,视图函数

  • 视图函数(模板函数)

    使用条件:beego支持用户定义视图函数,但是必须在beego.Run()调用之前。

    设置如下:

    • 先定义函数

      func hello(in string)(out string){
          out = in + "world"
          return
      }
      
    • 添加映射

      添加映射是把后台的函数名和视图中调用的函数名关联起来,两个名字可以不一样。用的方法是AddFuncMap(),第一个参数是视图中调用的函数名,第二个参数是后台的函数名

      beego.AddFuncMap("hi",hello)这一步必须在beego.Run()之前调用
      
    • 在视图中调用,有两种形式

      第一种调用视图函数

      {{.Content | hi}}
      

      注意,这里面的.Content是传递给函数的参数,类型要一致,函数的返回值将在这里显示,只能传递一个参数

      第二种调用视图函数

      {{hi .Content}}
      

      第二种方法刚好和第一种方法顺序反过来,是先写函数名,再写参数,如果参数比较多,可以一直往后写。这种方法在开发中也比较常用。

    • beego默认封装的视图函数

      函数名 函数作用 使用方法
      dateformat 实现了时间的格式化,返回字符串。 {{dateformat .Time “2006-01-02T15:04:05Z07:00”}}
      date 实现了类似 PHP 的 date 函数,可以很方便的根据字符串返回时间 。 {{date .T “Y-m-d H:i:s”}}
      compare 实现了比较两个对象的比较,如果相同返回 true,否者 false。 {{compare .A .B}}
      substr 实现了字符串的截取,支持中文截取的完美截取 {{substr .Str 0 30}}
      html2str 实现了把 html 转化为字符串,剔除一些 script、css 之类的元素,返回纯文本信息 。 {{html2str .Htmlinfo}}
      str2html 实现了把相应的字符串当作 HTML 来输出,不转义 {{str2html .Strhtml}}

      还有一些其他不常用的, 可以参考开发文档了解

  • 用视图函数实现获取上一页下一页页码

    • 定义函数

      因为函数要在beego.Run()之前执行,我们可以把函数直接定义在main.go中,定义函数如下:

      //获取下一页页码
      func ShowNextPage(pageIndex int)int{
          return pageIndex + 1
      }
      
      //获取上一页页码
      func ShowPrePage(pageIndex int)int{
          return pageIndex - 1
      }
      
    • 添加映射

      beego.AddFuncMap("next",ShowNextPage)
      beego.AddFuncMap("pre",ShowPrePage)
      
    • 在视图中调用

      我们这里用第二种调用视图函数的方法

       <li><a href="/ShowArticleList?pageIndex={{pre .pageIndex}}">上一页 </a> </li>
       <li> <a href="/ShowArticleList?pageIndex={{next .pageIndex}}">下一页</a></li>
      

      问题:显示之后,我们点击上一页下一页发现功能实现了,但是有一个问题,一直点击上一页页码能出现负值,一直点击下一页页码能超过总页码,那我们怎么解决呢?

    • 问题解决

      页码超出范围的问题,思路:只需要在获取上一页下一页页码的时候对页码做一个判断即可,代码如下:

      //获取下一页页码
      func ShowNextPage(pageIndex int,pageCount int)int{
          if pageIndex == pageCount{
              return pageIndex
          }
          return pageIndex + 1
      }
      
      //获取上一页页码
      func ShowPrePage(pageIndex int)int{
          if pageIndex == 1{
              return pageIndex
          }
          return pageIndex - 1
      }
      

    到这里我们的分页功能就完全实现了

5.5小结

6.查看文章详情

业务流程图如下:

1538715475866.png

首先我们还是需要设计一下查看详情的请求路径。分析可知,我们查看文章详情必须指定要查看哪一篇文章,所以我们在点击查看详情的时候需要把能够标识具体哪一篇文章的数据传递给后台,这里我们通过URL传值的方式,传递文章ID给后台,设计路由为/ShowArticleDetail?id=article.Id

6.1文章详情页面显示

  • 前端处理

    修改查看详情的超链接代码如下:

    <td><a href="ShowArticleDetail?id={{.Id}}">查看详情</a></td>
    
  • 修改路由文件,添加查看详情的路由匹配,然后指定控制和请求对应的方法,修改如下:

    beego.Router("/ShowArticleDetail",&controllers.ArticleController{},"get:ShowArticleDetail")
    
  • 实现ShowArticleDetail()函数

    首先呢,我们需要获取传递过来的文章id

    id,err := this.GetInt("id")
    

    然后做数据校验

    //数据校验
      if err != nil{
          beego.Info("请求路径错误")
          this.Redirect("/ShowArticleList",302)
          return
      }
    

    数据没问题的话,就根据文章id查询文章信息

    //查询数据
      o := orm.NewOrm()
      var article models.Article
      article.Id = id
      o.Read(&article)
    

    获取数据之后,指定视图,并给视图传递数据

    //传递数据给视图,并指定视图
      this.Data["article"] = article
      this.TplName = "content.html"
    

    完整代码如下

    //获取文章id
      id,err := this.GetInt("id")
    //数据校验
      if err != nil{
          beego.Info("请求路径错误")
          this.Redirect("/ShowArticleList",302)
          return
      }
    //查询数据
      o := orm.NewOrm()
      var article models.Article
      article.Id = id
      o.Read(&article)
    
    //传递数据给视图,并指定视图
      this.Data["article"] = article
      this.TplName = "content.html"
    

    访问浏览器,查看页面如下:

1538716777459.png

这时候页面显示的是假数据,我们修改视图文件,让页面显示的数据为我们添加的文章数据:

  • 视图文件修改,还没有添加的数据不做修改。

    <div class="pannel">
                <h3 class="review_title">文章详情</h3>
                <div class="form_group">
                    <label>文章标题:</label>
                    <p class="detail"><b>{{.article.ArtiName}}</b></p>
                </div>
                <div class="form_group">
                    <label>文章类型:</label>
                    <p class="detail">体育新闻</p>
                </div>
                <div class="form_group">
                    <label>文章内容:</label>
                    <p class="detail"><img src="{{.article.Aimg}}">{{.article.Acontent}}</p>
                </div>
                <div class="form_group">
                    <label>阅读次数:</label>
                    <p class="detail">{{.article.Acount}}</p>
                </div>
                <div class="form_group">
                    <label>最近浏览:</label>
                    <p class="detail">张三 | 李四 |</p>
                </div>
                <div class="form_group">
                    <label>创建时间:</label>
                    <p class="detail">{{.article.Atime.Format "2006-01-02-15-04-05"}}</p>
                    <span>{{.errmsg}}</span>
                </div>
    </div>
    

    保存之后再次刷新页面,显示如下:

1538717147605.png

我们查看详情页面的显示这部分就实现了。

6.2阅读次数增加

每次查看详情其实就是阅读次数的增加,我们需要在查看详情函数里面给阅读次数加一,代码如下:

//给查询出来的文章阅读次数加一
    article.Acount += 1
    o.Update(&article)

7.编辑文章内容

7.1编辑页面显示

业务流程图如下:

1538717467806.png

编辑页面显示和文章详情页面处理流程基本一样,也同样需要传递文章ID,先需要确定请求路径,这里我们设置请求路径为UpdateArticle?id=article.id,修改路由文件,代码如下:

beego.Router("/UpdateArticle",&controllers.ArticleController{},"get:ShowUpdateArticle")

然后在后台查询数据传递给视图,逻辑重复,就不详细分析了,代码如下:

//展示编辑文章界面
func(this*ArticleController)ShowUpdateArticle(){
    //获取文章id
    id,err := this.GetInt("id")
    //数据校验
    if err != nil{
        beego.Info("请求路径错误")
        this.Redirect("/ShowArticleList",302)
        return
    }
    //查询数据
    o := orm.NewOrm()
    var article models.Article
    article.Id = id
    o.Read(&article)
    

    //传递数据给视图,并指定视图
    this.Data["article"] = article
    this.TplName = "update.html"
}

前端数据展示代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>更新文章内容</title>
     <link rel="stylesheet" type="text/css" href="/static/css/reset.css">
    <link rel="stylesheet" type="text/css" href="/static/css/main.css">
</head>
<body>
    <div class="header">
        <a href="#" class="logo fl"><img src="/static/img/logo.png" alt="logo"></a>
        <a href="#" class="logout fr">退 出</a>
    </div>

    <div class="side_bar">
        <div class="user_info">
            <img src="/static/img/person.png" alt="张大山">
            <p>欢迎你 <em>李雷</em></p>
        </div>

        <div class="menu_con">
            <div class="first_menu active"><a href="javascript:;" class="icon02">文章管理</a></div>
            <ul class="sub_menu show">
                <li><a href="#" class="icon031">文章列表</a></li>
                <li><a href="#" class="icon032">添加文章</a></li>
                <li><a href="#" class="icon034">添加分类</a></li>
            </ul>
        </div>
    </div>

    <div class="main_body" id="main_body">
        <div class="breadcrub">
            当前位置:文章管理>编辑文章
        </div>
        <div class="pannel">
            <form name = logon >
            <h3 class="review_title">编辑文章</h3>
            <div class="form_group">
                <label>文章标题:</label>
                <input type="text" class="input_txt2" name = "articleName" value="{{.article.ArtiName}}">
            </div>
            <div class="form_group">
                <label>文章内容:</label>
                <textarea class="input_multxt" name="content">{{.article.Acontent}}</textarea>
            </div>
            <div class="form_group">
                <label>上传图片:</label>
                <img src="{{.article.Aimg}}">
                <input type="file" name="uploadname" class="input_file">
            </div>
            <div class="form_group indent_group line_top">
                <input type="submit" value="添 加" class="confirm">
                <span>{{.errmsg}}</span>
            </div>
        </form>
        </div>
    </div>
</body>
</html>

在浏览器输入http://192.168.110.73:8080/UpdateArticle?id=1,显示如下:

1538718026674.png

7.2编辑文章数据

这一步其实是对查询到的文章进行更新操作,我们还用获取页面时的请求路径UpdateArticle?id=article.id,但是请求改为post请求,form标签修改如下:

<form name = logon method="post" action="/UpdateArticle?id={{.article.Id}}" enctype="multipart/form-data">

这里需要上传图片,记得给form添加enctype属性

接着我们去路由文件里面给我们这个请求指定方法。

beego.Router("/UpdateArticle",&controllers.ArticleController{},"get:ShowUpdateArticle;post:HandleUpdate")

然后去实现HandleUpdate函数,过程仍然是获取数据,校验数据,更新数据,返回视图这几步,没有什么新的知识点,我们就不做详细分析,直接看代码:


//抽离上传文件函数
func UploadFile(filePath string,this beego.Controller)string{
    file,head,err :=this.GetFile(filePath)
    defer file.Close()

    if err != nil{
        beego.Info("上传图片错误,请重新添加!")
        return ""
    }
    //文件格式判断
    fileExt := path.Ext(head.Filename)
    if fileExt != ".jpg" && fileExt != ".png" && fileExt != ".jpeg"{
        beego.Info("上传图片格式不正确,请重新添加!")
        return ""
    }

    //文件大小判断
    if head.Size > 5000000{
        beego.Info("上传图片太大,请重新添加!")
        return ""
    }

    //避免文件重名
    fileName := time.Now().Format("2006-01-02-15-04-05")
    this.SaveToFile("uploadname","./static/img/"+fileName+fileExt)
    return "/static/img/"+fileName+fileExt
}
//处理更新数据
func(this*ArticleController)HandleUpdate(){
    //获取数据
    id,err := this.GetInt("id")
    articleName :=this.GetString("articleName")
    content := this.GetString("content")
    img := UploadFile("uploadname",this.Controller)

    //校验数据,如果数据出错,返回当前编辑页面
    if err !=nil || articleName =="" || content == "" || img == ""{
        beego.Info("编辑数据不完整")
        this.Redirect("/UpdateArticle?id="+strconv.Itoa(id),302)
        return
    }
    //更新数据
    o := orm.NewOrm()
    var article models.Article
    article.Id = id
    if err := o.Read(&article);err != nil{
        beego.Info("传递的文章id错误")
        this.Redirect("/UpdateArticle?id="+strconv.Itoa(id),302)
        return
    }
    article.ArtiName = articleName
    article.Acontent = content
    article.Aimg = img
    o.Update(&article)
    //返回视图
    this.Redirect("/ShowArticleList",302)
}

8.删除文章

8.1删除功能实现

业务流程图如下:

1538719480706.png

删除功能相比较前面的功能算是比较简单的,只需要传递过来文章id值,然后删除文章即可。

首先我们还是要指定删除文章的请求路径:DeleteArticle?id=article.Id

然后修改路由文件,为删除请求指定控制器,指定函数。

beego.Router("/DeleteArticle",&controllers.ArticleController{},"get:DeleteArticle")

然后在后台实现DeleteArticle函数,代码如下:

//删除文章
func(this*ArticleController)DeleteArticle(){
    //获取文章Id
    id,err := this.GetInt("id")
    if err != nil{
        beego.Info("删除文章请求路径错误")
        this.Redirect("/ShowArticleList",302)
        return
    }
    //删除文章
    o := orm.NewOrm()
    var article models.Article
    article.Id = id
    o.Delete(&article)

    //返回视图界面
    this.Redirect("/ShowArticleList",302)
}

这时候你发现功能实现了,但是存在误删的可能,整个页面显的特别不友好,我们给页面加个js提示,防止误删。

8.2删除js提示

业务分析:当点击删除超链接的时候,弹出对话框,如果确认就发送请求,如果取消,就不发送请求:代码如下:

<script type="text/javascript">
    $(".dels").click(function () {
        if(!confirm("是否确认删除?")){
            return false
        }
    })
</script>

显示效果如下:

1538720450645.png
版权声明:本文来源简书,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://www.jianshu.com/p/24f547615a31
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-01-12 11:59:38
  • 阅读 ( 2138 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢