網(wǎng)絡架構(gòu)中sdn是指關(guān)鍵字優(yōu)化
Beego案例-新聞發(fā)布系統(tǒng)
1.注冊
后臺代碼和昨天案例代碼一致。,所以這里面只寫一個注冊的業(yè)務流程圖。
**業(yè)務流程圖 **
2.登陸
業(yè)務流程圖
登陸和注冊業(yè)務和我們昨天登陸和注冊基本一樣,所以就不再重復寫這個代碼
但是我們遇到的問題是如何做代碼的遷移,把昨天的登陸和注冊拿過來直接用?
-
首先,我們需要把靜態(tài)頁面拷貝到我們項目目錄下面。
- 進入項目目錄,刪除掉原來的static文件夾
- 進入項目目錄,刪除掉原來的static文件夾
-
然后拷貝我們昨天課堂資料中的
static.zip
到這個目錄,并解壓,解壓之后如下圖:
- 打開static文件夾,顯示如下,則說明拷貝成功:
- 打開static文件夾,顯示如下,則說明拷貝成功:
-
把static文件夾中所有的html文件都拷貝到views文件夾下面(昨天的幾個頁面已經(jīng)沒用了,可以刪除),拷貝之后views文件顯示如下:
這時候用GoLand打開我們的項目,顯示如下:
-
打開
register.html
頁面,修改頁面中form表單的內(nèi)容-
給
<form>
標簽加上action="/register" method="post"
屬性 -
給兩個
<input>
標簽的name分別改為name="userName"
和name = "passwd"
- 表單相關(guān)代碼如下:
<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表單的內(nèi)容-
給
<form>
標簽加上action="/login" method="post"
屬性 -
給兩個
<input>
標簽的name分別改為name="userName"
和name = "passwd"
- 表單相關(guān)代碼如下:
<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>
(登陸界面多了個記住用戶名標簽,明天我們實現(xiàn)這個功能)
-
改完之后,運行項目,測試注冊和登陸頁面能夠能唱顯示,并且功能沒有問題說明代碼遷移成功。
3.創(chuàng)建數(shù)據(jù)庫
3.1數(shù)據(jù)庫表的設計
接下來我們就要實現(xiàn)文章相關(guān)的操作,所以這里我們要在數(shù)據(jù)庫中生成一個文章表。
我們以前在數(shù)據(jù)庫中創(chuàng)建表的時候,會給字段加很多限制屬性,比如非空,長度,默認值等等,在ORM中,創(chuàng)建表時也可以給各個字段添加相應的限制。那如何去加限制呢?我們先看例子:
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)"`
}
由上面的代碼可以看出,要給哪個字段添加屬性,需要在這個字段后面添加 `` 括起來的內(nèi)容,格式為orm:"限制條件"
。那這些限制條件都有哪些呢?我在這里給大家列了一個表格。
限制條件 | 作用 |
---|---|
pk | 設置該字段為主鍵 |
auto | 這只該字段自增,但是要求該字段必須為整型 |
default(0) | 設置該字段的默認值,需要注意字段類型和默認值類型一致 |
size(100) | 設置該字段長度為100個字節(jié),一般用來設置字符串類型 |
null | 設置該字段允許為空,默認不允許為空 |
unique | 設置該字段全局唯一 |
digits(12);decimals(4) | 設置浮點數(shù)位數(shù)和精度。比如這個是說,浮點數(shù)總共12位,小數(shù)位為四位。 |
auto_now | 針對時間類型字段,作用是保存數(shù)據(jù)的更新時間 |
auto_now_add | 針對時間類型字段,作用是保存數(shù)據(jù)的添加時間 |
注意:當模型定義里沒有主鍵時,符合int, int32, int64, uint, uint32, uint64 類型且名稱為 Id 的 Field 將被視為主鍵,能夠自增. "
Mysql中時間類型有date和datetime兩種類型,但是我們go里面只有time.time一種類型,如果項目里面要求精確的話,就需要指定類型,指定類型用的是type(date)或者type(datetime)
3.2生成表
這時候注意,我們添加了結(jié)構(gòu)體對象之后,并不能直接生成表,需要注冊,注冊的代碼就是初始化數(shù)據(jù)庫三行代碼中的第二行,注冊表結(jié)構(gòu),把要創(chuàng)建的表對應的結(jié)構(gòu)體對象作為函數(shù)的參數(shù),代碼如下:
orm.RegisterModel(new(User),new(Article))
創(chuàng)建之后,我們可以在goland下方查看創(chuàng)建表過程,也可以進入數(shù)據(jù)庫查看是否建表成功,成功的話,數(shù)據(jù)庫顯示如下:
登陸成功之后,訪問新聞列表展示頁面,但是我們現(xiàn)在還沒有新聞,所以我們先實現(xiàn)插入文章界面。
添加文字顯示實現(xiàn)
修改路由文件
在router.go文件的init函數(shù)中添加下面這一行代碼
//文章列表頁訪問
beego.Router("/showArticleList", &controllers.ArticleController{}, "get:ShowArticleList")
顯示文章界面的顯示
-
先創(chuàng)建一個article.go文件用來存放文章有關(guān)的業(yè)務代碼
-
然后在article.go文件中創(chuàng)建一個ArticleController控制器,并定義一個ShowArticleList函數(shù)代碼如下:
package controllersimport beego "github.com/beego/beego/v2/server/web"type ArticleController struct {beego.Controller
}func (this *ArticleController) ShowArticleList() {this.TplName = "index.html"
}
寫完代碼之后,我們從瀏覽器登錄以后跳轉(zhuǎn)http://localhost:8080/showArticleList
,如果能在瀏覽器中看到下面這個界面,表示頁面展示成功:
4.插入文章
業(yè)務流程圖
插入頁面我們用的視圖是add.html
,這里我們規(guī)定添加文章界面的請求路徑為/addArticle
4.1修改路由文件
在router.go文件的init函數(shù)中添加下面這一行代碼
beego.Router("/addArticle",&controllers.ArticleControlle{},"get:ShowAddArticle")
寫完代碼之后,我們從瀏覽器發(fā)出一個請求localhost:8080/addArticle
,如果能在瀏覽器中看到下面這個界面,表示頁面展示成功:
4.2添加文章界面的顯示
-
先創(chuàng)建一個article.go文件用來存放文章有關(guān)的業(yè)務代碼
-
然后在article.go文件中創(chuàng)建一個ArticleController控制器,并定義一個ShowAddArticle函數(shù)代碼如下:
import "github.com/astaxie/beego"type ArticleController struct {beego.Controller }func (this*ArticleController)ShowAddArticle(){}
-
接著我們來實現(xiàn)
ShowAddArticle
函數(shù),這個函數(shù)只是用來展示頁面的,所以我們只需要給他制定一個視圖就可以,代碼如下:func (this*ArticleController)ShowAddArticle(){this.TplName = "add.html" }
寫完代碼之后,我們從瀏覽器發(fā)出一個請求
localhost:8080/addArticle
,如果能在瀏覽器中看到下面這個界面,表示頁面展示成功:
4.3插入文章數(shù)據(jù)處理
上面我們顯示了添加文章界面,觀察界面可以發(fā)現(xiàn),我們需要獲取文章標題,文章類型, 文章內(nèi)容,上傳圖片。其中文章類型牽涉到多表操作,暫時只做簡單的單表操作。首先讓我們來看一下,插入頁面的前端部分修改。
4.3.1前端頁面修改
由頁面可知,我們這里面是要上傳數(shù)據(jù),所以我們這里需要一個form表單,打開前端界面add.html
,能看到我們這里面確實有一個標簽,只是沒有屬性,我們需要給標簽添加action和method屬性,這個請求還是添加文章,所以我們還可以用添加文章的請求路徑,設置action屬性action="/addArticle"
。因為上傳數(shù)據(jù),我們這里用post方法,設置method屬性method="post"
。其他部分不用修改。form修改代碼如下:
<form method="post" action="/addArticle">
4.3.2路由內(nèi)容修改
我們在前端添加了addArticle請求的post方法,所以需要修改一下router.go,給addArticle的post請求指定一個函數(shù),修改代碼如下:
beego.Router("/addArticle",&controllers.ArticleController{},"get:ShowAddArticle;post:HandleAddArticle")
4.3.3后臺代碼實現(xiàn)
有了函數(shù)名之后,我們就需要在后臺中實現(xiàn)這個函數(shù)。
-
首先是獲取數(shù)據(jù)
這時候我們看一下前端界面,我們需要獲取文章標題, 文章內(nèi)容,上傳圖片數(shù)據(jù),文章標題和文章內(nèi)容都是字符串,比較簡單,直接通過GetString獲取,所以我們先獲取這兩個內(nèi)容。通過查看add.html代碼我們發(fā)現(xiàn),文章標題對應的標簽name等于articleName,文章內(nèi)容對應的標簽name等于content(注意這里用的是textarea標簽,不是用的input,但是獲取數(shù)據(jù)方式一樣)。獲取數(shù)據(jù)的代碼如下:
//獲取數(shù)據(jù) articleName := this.GetString("articleName") content := this.GetString("content")
-
獲取數(shù)據(jù)之后就做數(shù)據(jù)校驗,我們這里還是做判空校驗
//對數(shù)據(jù)進行校驗 if articleName == ""|| content == ""{beego.Info("添加文章數(shù)據(jù)不完整")this.TplName = "add.html"return }
正常的添加流程,在校驗完數(shù)據(jù)之后就要把數(shù)據(jù)插入數(shù)據(jù)庫了,但是我們添加文章這個界面有點特殊,因為這里面牽涉到一個靜態(tài)文件的上傳,所以我們先處理靜態(tài)文件上傳功能。
-
靜態(tài)文件上傳(難點)
-
前端代碼
如果form表單中牽涉到文件上傳,在form表單中就需要添加一個屬性
enctype="multipart/form-data"
不然上傳就是假上傳,后臺不能獲取到上傳的文件。修改如下:<form method="post" action="/addArticle" enctype="multipart/form-data"><input type="file" class="input_file" name="uploadname">
-
后臺代碼修改
后臺接收上傳文件有兩個函數(shù)可以用。
GetFile(key string) (multipart.File, *multipart.FileHeader, error)
作用: 是獲取前端傳遞過來的文件。
參數(shù): 是input標簽中的name值
返回值: 有三個,一個是文件流(就是我們打開文件返回的內(nèi)容),第二個是文件相關(guān)信息,包括文件頭,文件大小,文件名字等,第三個是錯誤信息。示例代碼如下:
file,head,err := this.GetFile("uploadname")if err != nil{beego.Info("上傳圖片錯誤,請重新添加!")this.TplName = "add.html"return}
SaveToFile(fromfile, tofile string) error
作用:直接保存前端出過來的文件。
參數(shù): 有兩個參數(shù),第一個參數(shù)是前端標簽的name屬性值,第二個參數(shù)是文件在服務器端存儲的位置。**注意:這個位置字符串在前面需要加一個
.
返回值:是錯誤信息。示例代碼如下:
err := this.SaveToFile("uploadname","./static/img/1.jpg") if err != nil{beego.Info("上傳圖片錯誤,請重新添加!")this.TplName = "add.html"return}
-
-
在我們開發(fā)過程中,如果后臺接收文件并存儲需要做以下幾種判斷
文件格式判斷
我們通過GetFile可以獲取到文件名,然后通過path包,可以分離出文件的后綴,即文件格式,把你需要的文件格式過濾出來,不需要的返回即可。我們根據(jù)文件名獲取文件后綴,代碼如下:
//文件格式判斷fileExt := path.Ext(head.Filename)if fileExt != ".jpg" && fileExt != ".png" && fileExt != ".jpeg"{beego.Info("上傳圖片格式不正確,請重新添加!")this.TplName = "add.html"return}
文件大小的判斷
我們獲取文件之后,在存儲之前,文件流一般是在內(nèi)存中,所以文件不易過大,我們在這里做一個文件大小的判斷。代碼如下:
//文件大小判斷if head.Size > 5000000{beego.Info("上傳圖片太大,請重新添加!")this.TplName = "add.html"return}
避免文件重名
獲取文件之后我們要把文件存儲到服務器上,但是用戶可能會上傳同名的文件,如果文件同名的話,后來上傳的文件就把之前上傳的文件給覆蓋了,所以我們要給上傳的文件重新確定一個名字。這里我們以上傳文件時的時間作為上傳文件的文件名。默認的時間格式和我們常見的時間格式不一樣,所以這里我們需要對事件做一個格式化。格式化字符串為
"2006-01-02-15-04-05(規(guī)定的必須是這個,方便記憶可以用6-1-2-3-4-5來記)
代碼如下:fileName := time.Now().Format("2006-01-02-15-04-05") //存儲 this.SaveToFile("uploadname","./static/img/"+fileName+fileExt)
-
保存數(shù)據(jù)到數(shù)據(jù)庫
這里是數(shù)據(jù)的插入操作,我們不做詳細解釋,直接看代碼:
//插入數(shù)據(jù)庫//獲取orm對象o := orm.NewOrm()//獲取要插入的對象var article models.Article//給對象賦值article.ArtiName = articleNamearticle.Acontent = content//這一步需要注意,我們存儲的圖片是圖片地址,沒有 .article.Aimg = "/static/img/"+fileName+fileExt//插入o.Insert(&article)
-
返回視圖
如果沒有視圖,先返回一句話,代碼如下:
this.Ctx.WriteString("添加成功")
-
完整代碼如下:
//獲取數(shù)據(jù)articleName := this.GetString("articleName")content := this.GetString("content")//數(shù)據(jù)校驗if articleName == "" || content == ""{beego.Info("添加文章數(shù)據(jù)不完整,請重新輸入")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)//插入數(shù)據(jù)庫//獲取orm對象o := orm.NewOrm()//獲取要插入的對象var article models.Article//給對象賦值article.ArtiName = articleNamearticle.Acontent = content//這一步需要注意,我們存儲的圖片是圖片地址,沒有.article.Aimg = "/static/img/"+fileName+fileExt//插入o.Insert(&article)//返回視圖this.Ctx.WriteString("添加成功")
-
-
后臺完整代碼—添加文章內(nèi)容
package controllersimport ("github.com/beego/beego/v2/client/orm""github.com/beego/beego/v2/core/logs"beego "github.com/beego/beego/v2/server/web""path""projectName/models""time"
)type ArticleController struct {beego.Controller
}// 展示文章列表頁
func (this *ArticleController) ShowArticleList() {this.TplName = "index.html"
}// 展示添加文章頁面
func (this *ArticleController) ShowAddArticle() {this.TplName = "add.html"
}// 獲取添加文章數(shù)據(jù)
func (this *ArticleController) HandleAddArticle() {//獲取數(shù)據(jù) 這里獲取數(shù)據(jù)是前端name值articleName := this.GetString("articleName")content := this.GetString("content")select1 := this.GetString("select")//校驗數(shù)據(jù)if articleName == "" || content == "" {this.Data["errmsg"] = "添加數(shù)據(jù)不完整"this.TplName = "add.html"}logs.Info("chengpeng:", articleName, content, select1)//處理文件上傳 文件流,文件相關(guān)信息,錯誤file, head, err := this.GetFile("uploadname")defer file.Close()if err != nil {this.Data["errmsg"] = "文件上傳失敗"this.TplName = "add.html"return}//1 文件大小if head.Size > 50000000 {this.Data["errmsg"] = "文件太大,請重新上傳"this.TplName = "add.html"return}//文件格式//a.jpg 獲取后綴名ext := path.Ext(head.Filename)if ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {this.Data["errmsg"] = "文件格式錯誤,請重新上傳"this.TplName = "add.html"return}//防止重名fileName := time.Now().Format("2006-01-02-15-04-05") + ext//存儲/**第一個參數(shù)是前端標簽的name屬性值,第二個參數(shù)是文件在服務器端存儲的位置。**注意:這個位置字符串在前面需要加一個*/err = this.SaveToFile("uploadname", "./static/img/"+fileName)if err != nil {logs.Info("保存前端出過來的文件")return}//數(shù)據(jù)處理//插入操作newOrm := orm.NewOrm()var article models.Articlearticle.ArtiName = articleNamearticle.Acontent = contentarticle.Aimg = "/static/img/" + fileNamenewOrm.Insert(&article)//返回頁面this.Redirect("/showArticleList", 302)
}
添加文章路由代碼
package routersimport (beego "github.com/beego/beego/v2/server/web""projectName/controllers"
)func init() {//登錄刪除這個在上一個有寫//beego.Router("/", &controllers.MainController{}, "get:ShowGet;post:Post")//beego.Router("/register", &controllers.UserController{}, "get:ShowRegister;post:HandlePost")//beego.Router("/login", &controllers.UserController{}, "get:ShowLogin;post:HandleLogin")//文章列表頁訪問beego.Router("/showArticleList", &controllers.ArticleController{}, "get:ShowArticleList")//添加文字beego.Router("/addArticle", &controllers.ArticleController{}, "get:ShowAddArticle;post:HandleAddArticle")
}
5.新聞信息展示
添加文章之后我們回到文章顯示界面,我們這里固定顯示文章列表頁的請求為/ShowArticleList
,然后給這個請求指定控制器,以及相應的方法修改。
5.1修改路由文件
首先我們修改路由文件,代碼如下:
beego.Router("/ShowArticleList",&controllers.ArticleController{},"get:ShowArticleList")
5.2后臺代碼
修改路由文件之后,我們實現(xiàn)ShowArticleList函數(shù)。
5.2.1獲取所有文章
-
獲取orm對象
o := orm.NewOrm()
-
定義一個對象數(shù)組(切片),用來存儲獲取的所有對象
var articles []models.Article
-
指定要查詢的數(shù)據(jù)庫表,用QueryTable函數(shù),參數(shù)是表名,返回值是queryseter,ORM 以 QuerySeter 來組織查詢,每個返回 QuerySeter 的方法都會獲得一個新的 QuerySeter 對象。
qs := o.QueryTable("Article")
-
獲取所有數(shù)據(jù),用all方法,參數(shù)是對象數(shù)組地址
qs.All(&articles)
-
獲取數(shù)據(jù)之后把數(shù)據(jù)傳遞給視圖,并且指定視圖文件
this.Data["articles"] = articles this.TplName = "index.html"
在瀏覽器里面輸入地址之后能獲取下面頁面,表示代碼沒有問題
-
ORM高級查詢**(重點)**
我們在后面項目開發(fā)中對數(shù)據(jù)庫的查詢,一般都是指定數(shù)據(jù)庫表,用高級查詢的方法進行查詢。ORM支持如下幾種高級查詢。
函數(shù)名 作用 用法 Limit() 獲取部分數(shù)據(jù) 有兩個參數(shù),第一個參數(shù)是指定獲取幾條數(shù)據(jù),第二個參數(shù)指定從哪里獲取qs.Limit(size,start)。返回值還是qs OrderBy() 根據(jù)指定的字段排序 只有一個參數(shù),參數(shù)作用是指定按照哪個字段排序,返回值是qs Distinct() 去重 沒有參數(shù),返回值是qs Count() 查詢符合條件的數(shù)據(jù)條目數(shù) 沒有參數(shù),返回值是查詢到的條目數(shù)和錯誤信息 All() 把查詢到的數(shù)據(jù)全部存儲到指定的容器里面 只有一個參數(shù),指定存儲查詢對象的存儲容器 RelatedSel() 多表查詢的時候使用,指定關(guān)聯(lián)的數(shù)據(jù)庫表 參數(shù)長度不限,關(guān)聯(lián)幾個表,放幾個參數(shù) Filter() 過濾器,相當于SQL語句中的where 有兩個參數(shù),第一個參數(shù)是指定查詢條件,第二個參數(shù)是值 … … … 還有其他很多高級查詢,具體參考:https://beego.me/docs/mvc/model/query.md頁面查看
5.3前端代碼
5.3.1視圖循環(huán)語法
后臺傳遞給視圖的數(shù)據(jù)是對象數(shù)組,要訪問到每一個對象需要循環(huán)訪問這個數(shù)組,那我們來看一下這個循環(huán)語法。循環(huán)語法有兩種,一種格式如下:
{{range $index,$val := .articles}}{{$val}}
{{end}}
$index
表示的是下標,$val
表示的數(shù)組元素,循環(huán)的內(nèi)容放在range和end之間。
另外一種循環(huán)如下:
{{range .articles}}{{.Name}}
{{end}}
在range和end之間通過{{.}}直接獲取數(shù)組元素的字段值。
5.3.2視圖數(shù)據(jù)展示
了解了視圖的循環(huán)語法之后,我們就可以循環(huán)獲取控制器傳遞過來的對象數(shù)組數(shù)據(jù)。代碼如下:
{{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>財經(jīng)新聞</td></tr>
{{end}}
實現(xiàn)之后我們就可以把添加文章最后的跳轉(zhuǎn)改成顯示文章列表頁,超鏈接和文章類型,我們在接下來的頁面實現(xiàn)。
5.4數(shù)據(jù)的分頁顯示(難點)
觀察我們的列表頁可以發(fā)現(xiàn),我們文章里表下面是分頁展示,接著我們來實現(xiàn)這個分頁。分頁功能在我們平常瀏覽網(wǎng)頁的時候也比較常見,這里我們先實現(xiàn)簡單的分頁功能,等到我們項目實戰(zhàn)的時候會給大家封裝一個分頁函數(shù),實現(xiàn)一個高級點的分頁。首頁顯示如下:
分頁的好處:如果沒有分頁,我們訪問完數(shù)據(jù)就要全部在頁面顯示,有分頁之后我們可以顯示部分數(shù)據(jù),好處有一下兩點。
- 方便瀏覽,分頁瀏覽可以更方便我們平常訪問網(wǎng)頁。
- 提高訪問網(wǎng)站速度。如果一次性把數(shù)據(jù)全部從數(shù)據(jù)庫中取出來,效率沒有一次取出部分數(shù)據(jù)塊。
了解了上面的內(nèi)容之后我們開始寫代碼實現(xiàn)分頁的功能,一般開發(fā)中遇見這種大的功能模塊,我們都是劃分為幾個小塊,一點一點來實現(xiàn)。我們從簡單到復雜來實現(xiàn)相應功能,首先我們先獲取總頁數(shù)和總記錄數(shù)。
5.4.1獲取總記錄數(shù)和總頁數(shù)
首頁顯示代碼是ShowArticleList函數(shù),所以我們分頁的業(yè)務代碼也在這個函數(shù)里面。
-
獲取總記錄數(shù),orm是用count函數(shù)來獲取數(shù)據(jù)的記錄數(shù),沒有參數(shù),返回值為記錄數(shù)和錯誤信息,代碼如下:
count,err := qs.Count()
-
獲取總頁數(shù)
總頁數(shù) = 總記錄數(shù) / 每頁顯示的數(shù)據(jù)條數(shù)
總記錄數(shù)我們已經(jīng)獲取了,所以需要我們自己設置每頁顯示多少條數(shù)據(jù),然后相除就可以獲得,代碼如下:
//確定每頁顯示數(shù)pageSize := 2 //獲取總頁數(shù)pageCount := count / pageSize
-
把數(shù)據(jù)傳遞給視圖,并在視圖中顯示。
this.Data["count"] = count this.Data["pageCount"] = pageCount
這時候你會發(fā)現(xiàn),當你的最后一頁顯示的數(shù)據(jù)不滿的話,總頁數(shù)會少計算一頁,原因是我們求總頁數(shù)的計算是兩個整數(shù)相除,除不盡的時候會自動舍去小數(shù)位。這和我們真實的業(yè)務不相符。所以我們需要修改獲取總頁數(shù)的代碼。怎么修改呢?完全改成浮點數(shù)顯然也不行,因為總頁碼不會是小數(shù)。這里面我們用天花板函數(shù)Ceil()。Ceil()的作用是傳遞過來一個浮點數(shù),獲取比這個浮點數(shù)大的又離這個浮點數(shù)最近的整數(shù),代碼如下:
//獲取總頁數(shù)pageCount :=math.Ceil(float64(count) / float64(pageSize))
頁碼這時候顯示正確。
5.4.2獲取首頁和末頁數(shù)據(jù)
獲取完總頁數(shù)和總記錄數(shù)之后,最簡單的功能模塊就是首頁和末頁內(nèi)容的顯示。首頁和末頁,我們需要把相應的頁碼傳遞過來才能知道獲取哪些數(shù)據(jù)。那視圖如何給后臺傳遞數(shù)據(jù)呢?我們在平常瀏覽網(wǎng)頁的時候經(jīng)常會遇到類似于這樣的URL地址
http://tieba.baidu.com/f?fr=index&fp=0&ie=utf-8&red_tag=m2329796506
我們重點關(guān)注?后面的內(nèi)容,他們是成對出現(xiàn)的,每對之間用&
連接,這種是URL傳值的一種。我們在后臺通過GetString函數(shù)可以獲取到相應的值。
-
設置首頁的超鏈接
我們可以通過URL傳值的方式把頁碼傳遞過來。這里我們設置首頁的標簽超鏈接為
/ShowArticleList?pageIndex=1
-
獲取首頁數(shù)據(jù)
我們先通過GetString()獲取到頁碼,然后通過頁碼獲取相應的數(shù)據(jù)。這里給大家介紹數(shù)據(jù)庫獲取部分數(shù)據(jù)的函數(shù)**Limit() **
Limit()-----這個有bug如果寫在下面就會報錯–指定之后要查詢的數(shù)據(jù)庫表以后必須先查詢
**作用:**獲取數(shù)據(jù)庫中部分數(shù)據(jù)
**參數(shù):**第一個參數(shù)是獲取多少數(shù)據(jù),第二個參數(shù)是從哪里開始取數(shù)據(jù)
返回值是queryseter類型,示例代碼如下
qs := qs.Limit(pageSize,start)
我們掌握了limit函數(shù)之后,現(xiàn)在要獲取數(shù)據(jù)庫中部分數(shù)據(jù),pageSize我們已經(jīng)知道了,這個start怎么去求呢?我們可以根據(jù)start的規(guī)律來找,比如說,第一頁數(shù)據(jù)的起始位置是0,第二頁的其實位置是2,第三頁的其實位置是4,你發(fā)現(xiàn)起始位置剛好是頁碼減一乘以pageSize,由此我們得出公式。start = (pageIndex - 1) * pageSize
那么我們獲取首頁的代碼如下:
//獲取頁碼pageIndex,_ := this.GetInt("pageIndex") //確定數(shù)據(jù)的起始位置start := (pageIndex - 1) * pageSize //查詢數(shù)據(jù)庫部分數(shù)據(jù)qs.Limit(pageSize,start).All(&articles)
這時候有個問題,我們從其他頁面跳轉(zhuǎn)到首頁的時候沒有指定pageIndex,所以我們需要對獲取不到pageIndex的情況進行處理,**處理方案:**當沒有獲取到pageIndex的時候默認pageIndex等于1,即默認訪問首頁內(nèi)容。修改后的代碼如下:
//獲取頁碼pageIndex,err := this.GetInt("pageIndex")if err != nil{pageIndex = 1} //確定數(shù)據(jù)的起始位置start := (pageIndex - 1) * pageSize //查詢數(shù)據(jù)庫部分數(shù)據(jù)qs.Limit(pageSize,start).All(&articles)
-
獲取末頁數(shù)據(jù)只要參考著首頁,把傳過來的pageIndex改為總頁碼數(shù)即可。設置末頁的鏈接為
/ShowArticleList?pageIndex={{.pageCount}}
這時候記得把頁碼也傳遞給視圖
5.4.2獲取上一頁和下一頁數(shù)據(jù)
前面我們已經(jīng)獲取了首頁和末頁的數(shù)據(jù),仿照著鏈接,我們可以把上一頁下一頁的鏈接也實現(xiàn),設置上一頁的超鏈接為/ShowArticleList?pageIndex={{.pageIndex}} - 1
,但是你在index.html寫了這個之后,編輯器會報錯,html標簽屬性不能直接進行數(shù)學運算。這時候我們就要想辦法,不在視圖里面操作,并且給pageIndex減1,方法有很多,這里呢,老師給你們介紹一種beego處理這種簡單業(yè)務邏輯的方法,視圖函數(shù)
-
視圖函數(shù)(模板函數(shù))
**使用條件:**beego支持用戶定義視圖函數(shù),但是必須在beego.Run()調(diào)用之前。
設置如下:
-
先定義函數(shù)
func hello(in string)(out string){out = in + "world"return }
-
添加映射
添加映射是把后臺的函數(shù)名和視圖中調(diào)用的函數(shù)名關(guān)聯(lián)起來,兩個名字可以不一樣。用的方法是AddFuncMap(),第一個參數(shù)是視圖中調(diào)用的函數(shù)名,第二個參數(shù)是后臺的函數(shù)名
beego.AddFuncMap("hi",hello)這一步必須在beego.Run()之前調(diào)用
-
在視圖中調(diào)用,有兩種形式
第一種調(diào)用視圖函數(shù)
{{.Content | hi}}
注意,這里面的.Content是傳遞給函數(shù)的參數(shù),類型要一致,函數(shù)的返回值將在這里顯示,只能傳遞一個參數(shù)
第二種調(diào)用視圖函數(shù)
{{hi .Content}}
第二種方法剛好和第一種方法順序反過來,是先寫函數(shù)名,再寫參數(shù),如果參數(shù)比較多,可以一直往后寫。這種方法在開發(fā)中也比較常用。
-
beego默認封裝的視圖函數(shù)
函數(shù)名 函數(shù)作用 使用方法 dateformat 實現(xiàn)了時間的格式化,返回字符串。 {{dateformat .Time “2006-01-02T15:04:05Z07:00”}} date 實現(xiàn)了類似 PHP 的 date 函數(shù),可以很方便的根據(jù)字符串返回時間 。 {{date .T “Y-m-d H:i:s”}} compare 實現(xiàn)了比較兩個對象的比較,如果相同返回 true,否者 false。 {{compare .A .B}} substr 實現(xiàn)了字符串的截取,支持中文截取的完美截取 {{substr .Str 0 30}} html2str 實現(xiàn)了把 html 轉(zhuǎn)化為字符串,剔除一些 script、css 之類的元素,返回純文本信息 。 {{html2str .Htmlinfo}} str2html 實現(xiàn)了把相應的字符串當作 HTML 來輸出,不轉(zhuǎn)義 {{str2html .Strhtml}} 還有一些其他不常用的, 可以參考開發(fā)文檔了解
-
-
用視圖函數(shù)實現(xiàn)獲取上一頁下一頁頁碼
-
定義函數(shù)
因為函數(shù)要在beego.Run()之前執(zhí)行,我們可以把函數(shù)直接定義在main.go中,定義函數(shù)如下:
//獲取下一頁頁碼 func ShowNextPage(pageIndex int)int{return pageIndex + 1 }//獲取上一頁頁碼 func ShowPrePage(pageIndex int)int{return pageIndex - 1 }
-
添加映射
beego.AddFuncMap("next",ShowNextPage) beego.AddFuncMap("pre",ShowPrePage)
-
在視圖中調(diào)用
我們這里用第二種調(diào)用視圖函數(shù)的方法
<li><a href="/ShowArticleList?pageIndex={{pre .pageIndex}}">上一頁 </a> </li><li> <a href="/ShowArticleList?pageIndex={{next .pageIndex}}">下一頁</a></li>
問題:顯示之后,我們點擊上一頁下一頁發(fā)現(xiàn)功能實現(xiàn)了,但是有一個問題,一直點擊上一頁頁碼能出現(xiàn)負值,一直點擊下一頁頁碼能超過總頁碼,那我們怎么解決呢?
-
問題解決
頁碼超出范圍的問題,思路:只需要在獲取上一頁下一頁頁碼的時候?qū)摯a做一個判斷即可,代碼如下:
//獲取下一頁頁碼 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 }
到這里我們的分頁功能就完全實現(xiàn)了
-
完整后臺代碼
package controllersimport ("github.com/beego/beego/v2/client/orm""github.com/beego/beego/v2/core/logs"beego "github.com/beego/beego/v2/server/web""math""path""projectName/models""time"
)type ArticleController struct {beego.Controller
}// 展示文章列表頁
func (this *ArticleController) ShowArticleList() {//獲取數(shù)據(jù)//高級查詢pageSize := 2//獲取頁碼pageIndex, err := this.GetInt("pageIndex")if err != nil {pageIndex = 1}//起始位置計算start := (pageIndex - 1) * pageSize//指定表newOrm := orm.NewOrm()//指定要查詢的數(shù)據(jù)庫表queryset := newOrm.QueryTable("Article") //QuerySeter//查詢所有的 返回值查詢多少數(shù)據(jù)var articles []models.Article//有bug,寫下面就會報錯 expected 1 destination arguments in Scan, not 6_, err1 := queryset.Limit(pageSize, start).All(&articles)if err1 != nil {logs.Info("查詢數(shù)據(jù)錯誤", err1)return}//查詢總記錄數(shù)————總的記錄數(shù)count, err := queryset.Count()if err != nil {logs.Info("查詢總記錄數(shù)錯誤", err)return}//獲取總頁數(shù)pageCount := math.Ceil(float64(count) / float64(pageSize))//獲取數(shù)據(jù)//logs.Info("chengpengaini", pageIndex)//傳遞數(shù)據(jù)this.Data["pageIndex"] = pageIndexthis.Data["count"] = countthis.Data["pageCount"] = int(pageCount)this.Data["articles"] = articlesthis.TplName = "index.html"
}// 展示添加文章頁面
func (this *ArticleController) ShowAddArticle() {this.TplName = "add.html"
}// 獲取添加文章數(shù)據(jù)
func (this *ArticleController) HandleAddArticle() {//獲取數(shù)據(jù)articleName := this.GetString("articleName")content := this.GetString("content")select1 := this.GetString("select")//校驗數(shù)據(jù)if articleName == "" || content == "" {this.Data["errmsg"] = "添加數(shù)據(jù)不完整"this.TplName = "add.html"}logs.Info("chengpeng:", articleName, content, select1)//處理文件上傳 文件流,文件相關(guān)信息,錯誤file, head, err := this.GetFile("uploadname")defer file.Close()if err != nil {this.Data["errmsg"] = "文件上傳失敗"this.TplName = "add.html"return}if head.Filename == "" {this.Data["errmsg"] = "請上傳文件"this.TplName = "add.html"return}//1 文件大小if head.Size > 50000000 {this.Data["errmsg"] = "文件太大,請重新上傳"this.TplName = "add.html"return}//文件格式//a.jpg 獲取后綴名ext := path.Ext(head.Filename)if ext != ".jpg" && ext != ".png" && ext != ".bmp" && ext != ".jpeg" {this.Data["errmsg"] = "文件格式錯誤,請重新上傳"this.TplName = "add.html"return}//防止重名fileName := time.Now().Format("2006-01-02-15-04-05") + ext//存儲/**第一個參數(shù)是前端標簽的name屬性值,第二個參數(shù)是文件在服務器端存儲的位置。**注意:這個位置字符串在前面需要加一個*/err = this.SaveToFile("uploadname", "./static/img/"+fileName)if err != nil {logs.Info("保存前端出過來的文件")return}//數(shù)據(jù)處理//插入操作newOrm := orm.NewOrm()var article models.Articlearticle.ArtiName = articleNamearticle.Acontent = contentarticle.Aimg = "/static/img/" + fileNamenewOrm.Insert(&article)//返回頁面this.Redirect("/showArticleList", 302)
}
package mainimport (beego "github.com/beego/beego/v2/server/web"//項目運行期間,操作數(shù)據(jù)庫的代碼_ "projectName/models"_ "projectName/routers"
)func main() {beego.AddFuncMap("prepage", ShowPrePage)beego.AddFuncMap("nextpage", ShowNextPage)beego.Run()
}// 后臺定義函數(shù)----視圖函數(shù)
func ShowPrePage(pageIndex int) int {if pageIndex == 1 {return pageIndex}return pageIndex - 1
}func ShowNextPage(pageIndex int, pageCount int) int {if pageIndex == pageCount {return pageIndex}return pageIndex + 1
}
//文章列表頁訪問beego.Router("/showArticleList", &controllers.ArticleController{}, "get:ShowArticleList")//添加文字beego.Router("/addArticle", &controllers.ArticleController{}, "get:ShowAddArticle;post:HandleAddArticle")
}
add.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>添加文章內(nèi)容</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 method="post" action="/addArticle" enctype="multipart/form-data"><h3 class="review_title">添加文章</h3><div class="form_group"><label>文章標題:</label><input type="text" class="input_txt2" name="articleName" ></div><div class="form_group"><label>文章類型:</label><select class="sel_opt" name="select"><option>體育新聞</option><option>財經(jīng)新聞</option><option>科技新聞</option></select></div><div class="form_group"><label>文章內(nèi)容:</label><textarea class="input_multxt" name="content"></textarea></div><div class="form_group"><label>上傳圖片:</label><input type="file" class="input_file" name="uploadname"></div><div class="form_group indent_group line_top"><input type="submit" value="添 加" class="confirm"><span>{{.errmsg}}</span></div></form></div>
</div></body>
</html>
index.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"><script type="text/javascript" src="/static/js/jquery-1.12.4.min.js"></script></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="/addArticle" 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"><span class="sel_label">請選擇文章分類:</span><select name="select" id="select" class="sel_opt"><option selected="true">財經(jīng)新聞</option><option>體育新聞</option><option>科技新聞</option></select><table class="common_table"><tr><th width="43%">文章標題</th><th width="10%">文章內(nèi)容</th><th width="16%">添加時間</th><th width="7%">閱讀量</th><th width="7%">刪除</th><th width="7%">編輯</th><th width="10%">文章類型</th></tr>{{range $index,$val := .articles}}<tr><td>{{$val.ArtiName}}</td><td><a href="#">查看詳情</a></td><td> {{$val.Atime.Format "2006-01-02-15:04:05"}}</td><td>{{$val.Acount}}</td><td><a href="#" class="dels">刪除</a></td><td><a href="#">編輯</a></td><td>財經(jīng)新聞</td></tr>{{end}}</table><ul class="pagenation"><li><a href="/showArticleList?pageIndex=1">首頁</a></li><li><a href="/showArticleList?pageIndex={{.pageIndex | prepage }}">上一頁 </a> </li><li><a href="/showArticleList?pageIndex={{nextpage .pageIndex .pageCount}}">下一頁</a></li><li><a href="/showArticleList?pageIndex={{.pageCount}}">末頁</a></li><li>共{{.count}}條記錄/共{{.pageCount}}頁/當前{{.pageIndex}}頁</li></ul></div></div>
</body>
</html>
6.查看文章詳情
業(yè)務流程圖如下:
首先我們還是需要設計一下查看詳情的請求路徑。分析可知,我們查看文章詳情必須指定要查看哪一篇文章,所以我們在點擊查看詳情的時候需要把能夠標識具體哪一篇文章的數(shù)據(jù)傳遞給后臺,這里我們通過URL傳值的方式,傳遞文章ID給后臺,設計路由為/ShowArticleDetail?id=article.Id
6.1文章詳情頁面顯示
-
前端處理
修改查看詳情的超鏈接代碼如下:
<td><a href="ShowArticleDetail?id={{.Id}}">查看詳情</a></td>
-
修改路由文件,添加查看詳情的路由匹配,然后指定控制和請求對應的方法,修改如下:
beego.Router("/ShowArticleDetail",&controllers.ArticleController{},"get:ShowArticleDetail")
-
實現(xiàn)ShowArticleDetail()函數(shù)
首先呢,我們需要獲取傳遞過來的文章id
id,err := this.GetInt("id")
然后做數(shù)據(jù)校驗
//數(shù)據(jù)校驗if err != nil{beego.Info("請求路徑錯誤")this.Redirect("/ShowArticleList",302)return}
數(shù)據(jù)沒問題的話,就根據(jù)文章id查詢文章信息
//查詢數(shù)據(jù)o := orm.NewOrm()var article models.Articlearticle.Id = ido.Read(&article)
獲取數(shù)據(jù)之后,指定視圖,并給視圖傳遞數(shù)據(jù)
//傳遞數(shù)據(jù)給視圖,并指定視圖this.Data["article"] = articlethis.TplName = "content.html"
完整代碼如下
//獲取文章idid,err := this.GetInt("id") //數(shù)據(jù)校驗if err != nil{beego.Info("請求路徑錯誤")this.Redirect("/ShowArticleList",302)return} //查詢數(shù)據(jù)o := orm.NewOrm()var article models.Articlearticle.Id = ido.Read(&article)//傳遞數(shù)據(jù)給視圖,并指定視圖this.Data["article"] = articlethis.TplName = "content.html"
訪問瀏覽器,查看頁面如下:
這時候頁面顯示的是假數(shù)據(jù),我們修改視圖文件,讓頁面顯示的數(shù)據(jù)為我們添加的文章數(shù)據(jù):
-
視圖文件修改,還沒有添加的數(shù)據(jù)不做修改。
<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>文章內(nèi)容:</label><p class="detail"><img src="{{.article.Aimg}}">{{.article.Acontent}}</p></div><div class="form_group"><label>閱讀次數(shù):</label><p class="detail">{{.article.Acount}}</p></div><div class="form_group"><label>最近瀏覽:</label><p class="detail">張三 | 李四 |</p></div><div class="form_group"><label>創(chuàng)建時間:</label><p class="detail">{{.article.Atime.Format "2006-01-02-15-04-05"}}</p><span>{{.errmsg}}</span></div> </div>
保存之后再次刷新頁面,顯示如下:
我們查看詳情頁面的顯示這部分就實現(xiàn)了。
6.2閱讀次數(shù)增加
每次查看詳情其實就是閱讀次數(shù)的增加,我們需要在查看詳情函數(shù)里面給閱讀次數(shù)加一,代碼如下:
//給查詢出來的文章閱讀次數(shù)加一article.Acount += 1o.Update(&article)
7.編輯文章內(nèi)容
7.1編輯頁面顯示
業(yè)務流程圖如下:
編輯頁面顯示和文章詳情頁面處理流程基本一樣,也同樣需要傳遞文章ID,先需要確定請求路徑,這里我們設置請求路徑為UpdateArticle?id=article.id
,修改路由文件,代碼如下:
beego.Router("/UpdateArticle",&controllers.ArticleController{},"get:ShowUpdateArticle")
然后在后臺查詢數(shù)據(jù)傳遞給視圖,邏輯重復,就不詳細分析了,代碼如下:
//展示編輯文章界面
func(this*ArticleController)ShowUpdateArticle(){//獲取文章idid,err := this.GetInt("id")//數(shù)據(jù)校驗if err != nil{beego.Info("請求路徑錯誤")this.Redirect("/ShowArticleList",302)return}//查詢數(shù)據(jù)o := orm.NewOrm()var article models.Articlearticle.Id = ido.Read(&article)//傳遞數(shù)據(jù)給視圖,并指定視圖this.Data["article"] = articlethis.TplName = "update.html"
}
前端數(shù)據(jù)展示代碼如下:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>更新文章內(nèi)容</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>文章內(nèi)容:</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
,顯示如下:
7.2編輯文章數(shù)據(jù)
這一步其實是對查詢到的文章進行更新操作,我們還用獲取頁面時的請求路徑UpdateArticle?id=article.id
,但是請求改為post請求,form標簽修改如下:
<form name = logon method="post" action="/UpdateArticle?id={{.article.Id}}" enctype="multipart/form-data">
這里需要上傳圖片,記得給form添加enctype屬性
接著我們?nèi)ヂ酚晌募锩娼o我們這個請求指定方法。
beego.Router("/UpdateArticle",&controllers.ArticleController{},"get:ShowUpdateArticle;post:HandleUpdate")
然后去實現(xiàn)HandleUpdate函數(shù),過程仍然是獲取數(shù)據(jù),校驗數(shù)據(jù),更新數(shù)據(jù),返回視圖這幾步,沒有什么新的知識點,我們就不做詳細分析,直接看代碼:
//抽離上傳文件函數(shù)
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
}
//處理更新數(shù)據(jù)
func(this*ArticleController)HandleUpdate(){//獲取數(shù)據(jù)id,err := this.GetInt("id")articleName :=this.GetString("articleName")content := this.GetString("content")img := UploadFile("uploadname",this.Controller)//校驗數(shù)據(jù),如果數(shù)據(jù)出錯,返回當前編輯頁面if err !=nil || articleName =="" || content == "" || img == ""{beego.Info("編輯數(shù)據(jù)不完整")this.Redirect("/UpdateArticle?id="+strconv.Itoa(id),302)return}//更新數(shù)據(jù)o := orm.NewOrm()var article models.Articlearticle.Id = idif err := o.Read(&article);err != nil{beego.Info("傳遞的文章id錯誤")this.Redirect("/UpdateArticle?id="+strconv.Itoa(id),302)return}article.ArtiName = articleNamearticle.Acontent = contentarticle.Aimg = imgo.Update(&article)//返回視圖this.Redirect("/ShowArticleList",302)
}
8.刪除文章
8.1刪除功能實現(xiàn)
業(yè)務流程圖如下:
刪除功能相比較前面的功能算是比較簡單的,只需要傳遞過來文章id值,然后刪除文章即可。
首先我們還是要指定刪除文章的請求路徑:DeleteArticle?id=article.Id
然后修改路由文件,為刪除請求指定控制器,指定函數(shù)。
beego.Router("/DeleteArticle",&controllers.ArticleController{},"get:DeleteArticle")
然后在后臺實現(xiàn)DeleteArticle函數(shù),代碼如下:
//刪除文章
func(this*ArticleController)DeleteArticle(){//獲取文章Idid,err := this.GetInt("id")if err != nil{beego.Info("刪除文章請求路徑錯誤")this.Redirect("/ShowArticleList",302)return}//刪除文章o := orm.NewOrm()var article models.Articlearticle.Id = ido.Delete(&article)//返回視圖界面this.Redirect("/ShowArticleList",302)
}
這時候你發(fā)現(xiàn)功能實現(xiàn)了,但是存在誤刪的可能,整個頁面顯的特別不友好,我們給頁面加個js提示,防止誤刪。
8.2刪除js提示
業(yè)務分析:當點擊刪除超鏈接的時候,彈出對話框,如果確認就發(fā)送請求,如果取消,就不發(fā)送請求:代碼如下:
<script type="text/javascript">$(".dels").click(function () {if(!confirm("是否確認刪除?")){return false}})
</script>
1.類型相關(guān)內(nèi)容
在實現(xiàn)類型相關(guān)業(yè)務之前,我們先創(chuàng)建類型表。這里我們添加上一對多多對多的關(guān)系。
一個類型下面有很多篇文章,但是一篇文章只屬于一個類型,所以文章與類型屬于一對多。
同時我們分析,一個用戶可以閱讀多篇文章,一篇文章也可以被多個用戶閱讀,所以文章和用戶之間屬于多對多關(guān)系。
由此,我們開始建表,建表代碼如下,我們根據(jù)代碼分析一對多,多對多如何設置:
type User struct {Id intName string `orm:"unique"`Passwd string `orm:"size(20)"`Articles []*Article `orm:"rel(m2m)"` //設置多對多關(guān)系
}
//文章結(jié)構(gòu)體
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)"`ArticleType*ArticleType `orm:"rel(fk)"` //設置一對多關(guān)系Users []*User `orm:"reverse(many)"` //設置多對多的反向關(guān)系
}
//類型表
type ArticleType struct {Id intTname string `orm:"size(20)"`Articles []*Article `orm:"reverse(many)"` //設置一對多的反向關(guān)系
}func init(){//1.連接數(shù)據(jù)庫orm.RegisterDataBase("default","mysql","root:123456@tcp(127.0.0.1:3306)/test?charset=utf8")//2.注冊表orm.RegisterModel(new(User),new(Article),new(ArticleType))//3.生成表//1.數(shù)據(jù)庫別名//2.是否強制更新//3.創(chuàng)建表過程是否可見orm.RunSyncdb("default",false,true)
}
根據(jù)我們以前學過數(shù)據(jù)庫知識,表與表之間有幾種關(guān)系?一般有三種,一對一,一對多,多對多,但是我們開發(fā)中常用的是一對多和多對多,這里我們重點掌握這兩種,了解一對一即可。
orm中如何設置兩個表之間的關(guān)系呢?
如果兩個表之間有關(guān)系,ORM通過在兩個表對應的結(jié)構(gòu)體中添加對象指針或者對象指針數(shù)組來把兩個表之間關(guān)聯(lián)起來,并且在對象指針和對象指針數(shù)組字段添加上相應的屬性,比如我們上面的文章表和類型表屬于一對多,就需要在文章結(jié)構(gòu)體中添加一個類型的對象指針,然后設置一對多關(guān)系(orm:“rel(fk)”),同樣的,在類型表里面需要有一個文章的對象指針數(shù)組,并且設置一對多的反向關(guān)系(orm:“reverse(many)”)。
-
**一對一 **
關(guān)系設置:兩個對應的結(jié)構(gòu)體中都添加對方的結(jié)構(gòu)體指針,然后設置一對一關(guān)系(orm:“rel(one)”),反向關(guān)系設置為orm:“reverse(one)”
-
**一對多 **
關(guān)系設置:一對多中兩表之間的關(guān)系不可互換,以文章表和類型表為例,當創(chuàng)建表的時候
-
在 文章表對應的文章結(jié)構(gòu)體中添加類型表的對象指針,并且設置一對多關(guān)系(orm:“rel(fk)”),
-
在 類型張表對應的結(jié)構(gòu)體中添加文章表的對象指針數(shù)組,并且設置一對多的反向關(guān)系(orm:“reverse(many)”)
生成表的時候,數(shù)據(jù)庫會自動在 文章表中添加類型表的表的Id作為文章表的外鍵。如圖:
一對多插入操作:只需要在文章表插入類型對象即可。代碼如下:o := orm.NewOrm() article := models.Article{} artiType := models.ArticleType{Id:id} o.Read(&artiType) article.ArticleType = &artiType o.Insert(&article)
一對多查詢: ORM做多表查詢的時候默認是惰性查詢,即不明確指出來要做多表查詢,即便是兩個表之間存在關(guān)系,ORM也不會給兩個表做關(guān)聯(lián)。指定多表查詢的函數(shù)是RelatedSel()。參數(shù)是要關(guān)聯(lián)的表名,可以有多個。代碼如下:
count,err = o.QueryTable("Article").RelatedSel("ArticleType").Count()
如果關(guān)聯(lián)表的那個字段沒有值,那么數(shù)據(jù)查不到
-
-
多對多
關(guān)系設置:多對多中兩表之間的關(guān)系是平等的,所以他們的屬性設置可以呼喚,以文章表和用戶表為例,當創(chuàng)建表的時候
-
在 文章表對應的文章結(jié)構(gòu)體中添加用戶表的對象指針數(shù)組,并且設置多對多關(guān)系(orm:“rel(m2m)”),
-
在用戶表對應的結(jié)構(gòu)體中添加文章表的對象指針數(shù)組,并且設置多對多的反向關(guān)系(orm:“reverse(many)”)
生成表的時候,數(shù)據(jù)庫會生成一個用戶和文章之間的關(guān)系表,有三個字段,Id,用戶表Id,文章表ID。如下圖:
多對多插入操作:o := orm.NewOrm() //1.獲取操作對象 arti:= Article{Id: 1} //獲取article的多對多操作對象 m2m := o.QueryM2M(&arti, "Users")//第一個參數(shù)對象必須有主鍵,第二個參數(shù)是字段名 //獲取要插入的對象 user := &User{Id:1} o.Read(&user) //多對多對象插入 num, err := m2m.Add(user)//參數(shù)可以為對象,指針,對象數(shù)組,指針數(shù)組
多對多查詢:
有兩種方法:
第一種:直接用read查詢,然后加上LoadRelated ()函數(shù)來關(guān)聯(lián)兩張表。代碼如下:
post := Post{Id: 1} err := o.Read(&post) num, err := o.LoadRelated(&post, "Tags")
優(yōu)點是簡單,快捷。
缺點是返回值不是queryseter,不能調(diào)用其他的高級查詢。
第二種方法,是通過過濾器查詢,指定表之后,用Filter()過濾相應的條件,第一個參數(shù)是
表示另一張表的字段__另外一張表的表名__比較的字段
(注意是雙下劃線),第二個字段是要比較的值,需要注意的是這個順序是和表的插入順序相反的。代碼如下: -
1.1添加類型
分析過多表之間的操作之后,我們來實現(xiàn)類型有關(guān)的業(yè)務,首先我們需要先添加類型。
1.1.1添加類型頁面顯示
-
確定添加類型顯示的請求路徑為
/AddArticleType
-
在路由文件中添加相關(guān)代碼。
beego.Router("/addArticleType",&controllers.ArticleController{},"get:ShowAddType")
-
然后去控制器中實現(xiàn)ShowAddType函數(shù),先簡單的指定視圖。代碼如下:
//展示添加文章類型頁面 func(this*ArticleController)ShowAddType(){this.TplName = "addType.html" }
-
然后在瀏覽器輸入請求http://192.168.110.74:8080/addArticleType,頁面顯示如下:
-
由頁面可知,我們添加文章類型界面,分兩塊,一塊是上面以表格的形式顯示所有類型,一塊是下面增加分類。我們先來處理增加分類。
1.1.2添加類型數(shù)據(jù)處理
添加類型業(yè)務比較簡單,首先是修改我們的視圖頁面內(nèi)容,給form標簽請求方式和請求路徑,代碼如下:
<form method="post" action="/HandleAddType">
接著我們要修改路由文件,給請求指定控制器,指定方法:
beego.Router("/addArticleType",&controllers.ArticleController{},"get:ShowAddType;post:HandleAddType")
然后我們實現(xiàn)一下后臺處理函數(shù),這個函數(shù)的實現(xiàn)步驟和以前實現(xiàn)添加文章的步驟一樣,代碼處理還更簡單,不詳細分析,我們直接看代碼:
//處理添加文章類型數(shù)據(jù)
func(this*ArticleController)HandleAddType(){//獲取數(shù)據(jù)typeName := this.GetString("typeName")//數(shù)據(jù)校驗if typeName == ""{beego.Info("添加數(shù)據(jù)失敗")return}//插入數(shù)據(jù)庫o := orm.NewOrm()var articleType models.ArticleTypearticleType.Tname = typeNameif _,err :=o.Insert(&articleType);err != nil{beego.Info("添加數(shù)據(jù)失敗")return}//返回視圖this.TplName = "addType.html"
}
這里我們用渲染的方式返回視圖合適不合適,思考一下!
1.1.3查詢類型數(shù)據(jù)
現(xiàn)在我們類型表有數(shù)據(jù)了,可以在顯示頁面的時候把數(shù)據(jù)填充在頁面上
-
后臺代碼
//展示添加文章類型頁面 func(this*ArticleController)ShowAddType(){//查詢數(shù)據(jù)o := orm.NewOrm()var articleTypes []models.ArticleTypeo.QueryTable("ArticleType").All(&articleTypes)//傳遞數(shù)據(jù)給視圖并指定視圖this.Data["articleTypes"] = articleTypesthis.TplName = "addType.html" }
-
視圖代碼
在視圖頁面中,我們循環(huán)控制器傳遞過來的數(shù)組,拿到我們需要的數(shù)據(jù)
{{range .articleTypes}}<tr><td>{{.Id}}</td><td>{{.Tname}}</td><td><a href="javascript:;" class="edit">刪除</a></td></tr> {{end}}
這時候我們在瀏覽器輸入地址
http://192.168.110.75:8080/addArticleType
,得到如下頁面:
添加一個類型測試,然后發(fā)現(xiàn)頁面還是沒有類型顯示,這個說明我們代碼處理出問題了,哪里出問題了呢?還記得前面給大家留的思考題嗎?我們添加完文章類型之后,是直接渲染加載了視圖,這時候并沒有給視圖傳遞數(shù)據(jù),所以也就沒有類型顯示。這樣的結(jié)果和我們的業(yè)務 不符合,所以我們需要把添加完類型之后跳轉(zhuǎn)頁面的方式改為重定向,然后再看結(jié)果,發(fā)現(xiàn)類型顯示正常。1.2首頁根據(jù)下拉框選項不同,獲取不同類型數(shù)據(jù)
現(xiàn)在有類型數(shù)據(jù)了,我們添加文章的時候也需要添加上類型了。
1.2.1添加帶類型的文章
-
在展示頁面的時候需要把類型數(shù)據(jù)綁定添加類型的下拉框
-
后臺獲取數(shù)據(jù)(在展示添加文章界面那個函數(shù)里面寫相關(guān)代碼)
//展示添加文章界面 func (this*ArticleController)ShowAddArticle(){//查詢數(shù)據(jù)o := orm.NewOrm()var articleTypes []models.ArticleTypeo.QueryTable("ArticleType").All(&articleTypes)//傳遞數(shù)據(jù)給視圖并指定視圖this.Data["articleTypes"] = articleTypesthis.TplName = "add.html" }
-
視圖展示數(shù)據(jù)
循環(huán)獲取數(shù)據(jù),在下拉框中顯示類型名稱
<select class="sel_opt" name="select">{{range .articleTypes}}<option>{{.Tname}}</option>{{end}} </select>
-
-
添加文章的時候指定文章類型,代碼如下:
//給文章對象指定文章類型var articleType models.ArticleTypearticleType.Tname = typeNameo.Read(&articleType,"Tname")article.ArticleType = &articleType//插入o.Insert(&article)
1.2.2列表頁展示文章時,展示類型信息。
-
查詢所有問章,關(guān)聯(lián)文章類型表(查詢的時候加上RelatedSel(“ArticleType”)),代碼如下:
qs.Limit(pageSize,start).RelatedSel("ArticleType").All(&articles)
顯示的時候顯示出來
{{range .articles}}<tr><td>{{.ArtiName}}</td><td><a href="ShowArticleDetail?id={{.Id}}">查看詳情</a></td><td> {{.Atime.Format "2006-01-02-15-04-05"}}</td><td>{{.Acount}}</td><td><a href="/DeleteArticle?id={{.Id}}" class="dels">刪除</a></td><td><a href="UpdateArticle?id={{.Id}}">編輯</a></td><td>{{.ArticleType.Tname}}</td></tr> {{end}}
這時候你發(fā)現(xiàn),以前添加的文章都沒有顯示,還記得我們前面介紹多表操作的時候介紹的嗎,加上RelatedSel之后,如果相應的字段沒有數(shù)據(jù),將查詢不出來。
1.2.3根據(jù)下拉框選項不同,獲取不同類型數(shù)據(jù)(難點)
-
查詢類型數(shù)據(jù),并把數(shù)據(jù)綁定到下拉框
這個業(yè)務代碼和添加文章的業(yè)務代碼一樣,我們就不做詳細分析,直接看代碼:
//查詢數(shù)據(jù)var articleTypes []models.ArticleTypeo.QueryTable("ArticleType").All(&articleTypes)this.Data["articleTypes"] = articleTypes
視圖代碼:
<select name="select" id="select" class="sel_opt">{{range .articleTypes}}<option selected="true">{{.Tname}}</option>{{end}} </select>
-
根據(jù)下拉框選中類型,獲取相同類型的文章
-
把選中的類型數(shù)據(jù)傳遞給后臺
我們以前傳遞數(shù)據(jù)是用form表單,這里我們還是用form表單把下拉框包起來,然后把選中的數(shù)據(jù)傳遞給后臺。代碼如下:
<form method="get" action="/ShowArticleList"><select name="select" id="select" class="sel_opt">{{range .articleTypes}}<option selected="true">{{.Tname}}</option>{{end}}</select> </form>
思考,我們?yōu)槭裁从胓et請求不用post請求
這里沒有發(fā)送請求按鈕(盡量不要改美工設計的頁面),我們通過js代碼發(fā)送請求,js代碼如下:
$("#select").change(function () {$("#form").submit() })
-
根據(jù)獲取的類型,查詢有多少條數(shù)據(jù),以及顯示相同類型的文章
-
獲取前端傳遞過來的數(shù)據(jù)
//獲取類型名稱typeName := this.GetString("select")
-
根據(jù)類型,查詢有多少條符合條件的數(shù)據(jù),但是,需要注意這里面要考慮沒有傳遞類型名稱的請求,所以需要做個判斷,代碼如下:
//獲取類型名稱 typeName := this.GetString("select") //查詢數(shù)據(jù),以及分頁顯示 o := orm.NewOrm() qs := o.QueryTable("Article") var count int64 //數(shù)據(jù)校驗 if typeName == ""{count,_ =qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Count() }else {count,_ =qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Count() }
-
其他處理分頁的業(yè)務代碼不變,代碼如下:
//確定每頁顯示數(shù) pageSize := 2 //獲取總頁數(shù) pageCount :=math.Ceil(float64(count) / float64(pageSize)) //獲取頁碼 pageIndex,err := this.GetInt("pageIndex") if err != nil{pageIndex = 1 } //確定數(shù)據(jù)的起始位置 start := (pageIndex - 1) * pageSize
-
根據(jù)類型查詢相同類型的數(shù)據(jù),同樣需要做一個判斷。代碼如下:
//查詢相應類型的數(shù)據(jù) var articles []models.Article if typeName ==""{qs.RelatedSel("ArticleType").Limit(pageSize,start).All(&articles) }else {qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Limit(pageSize,start).All(&articles) }
-
其他代碼不變,獲取列表頁完整代碼如下:
func(this*ArticleController)ShowArticleList(){//獲取類型名稱typeName := this.GetString("select")//查詢數(shù)據(jù),以及分頁顯示o := orm.NewOrm()qs := o.QueryTable("Article")var count int64//數(shù)據(jù)校驗sif typeName == ""{count,_ =qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Count()}else {count,_ =qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Count()}//確定每頁顯示數(shù)pageSize := 2//獲取總頁數(shù)pageCount :=math.Ceil(float64(count) / float64(pageSize))//獲取頁碼pageIndex,err := this.GetInt("pageIndex")if err != nil{pageIndex = 1}//確定數(shù)據(jù)的起始位置start := (pageIndex - 1) * pageSize//查詢相應類型的數(shù)據(jù)var articles []models.Articleif typeName ==""{qs.RelatedSel("ArticleType").Limit(pageSize,start).All(&articles)}else {qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Limit(pageSize,start).All(&articles)}//查詢數(shù)據(jù)庫部分數(shù)據(jù)//獲取類型數(shù)據(jù)//查詢數(shù)據(jù)var articleTypes []models.ArticleTypeo.QueryTable("ArticleType").All(&articleTypes)this.Data["articleTypes"] = articleTypesthis.Data["count"] = countthis.Data["pageCount"] = int(pageCount)this.Data["pageIndex"] = pageIndex//傳遞數(shù)據(jù)并指定視圖this.Data["articles"] = articlesthis.TplName = "index.html" }
這時候你再看頁面,會發(fā)現(xiàn)一個問題,有一個選項一直都不能夠選中。為什么呢?
是因為,我們每一次改變下拉框的選項,都會讓js發(fā)出get請求給后臺,后臺就會重新查詢所有的類型表綁定下拉框,所以每次顯示的都是一個數(shù)據(jù),這樣的話,我們選中顯示的那條數(shù)據(jù),就無法觸發(fā)js發(fā)送請求,因為js認為下拉框顯示內(nèi)容并沒有變化。這時候下拉框顯示也有問題,那怎么解決這個問題呢?
1.2.4解決下拉框選項顯示的問題
-
-
? 通過前面的分析,我們知道每次下拉框都是重新從數(shù)據(jù)庫中獲取類型數(shù)據(jù)進行綁定,這里面我們就需要對選中的類型加一個判斷,當從數(shù)據(jù)庫中取出的數(shù)據(jù)是選中的類型時,就給下拉框選項屬性selected設置為true。首先后臺要傳遞當前選中的類型名稱給視圖,代碼如下:
//傳遞當下拉框選擇的類型名給視圖
this.Data["typeName"] = typeName
-
前端代碼處理
視圖中我們接收控制器傳遞過來的當前選中類型,然后與數(shù)據(jù)庫中的類型名進行比較,如果相同就設置選中不同就不設置,代碼如下:
<select name="select" id="select" class="sel_opt">{{range .articleTypes}}{{if compare .Tname $.typeName}}<option selected="true">{{.Tname}}</option>{{else}}<option>{{.Tname}}</option>{{end}}{{end}} </select>
需要注意的是,如果是在循環(huán)中獲取控制器傳遞過來的數(shù)據(jù),不能直接用
.
,要用$.
然后刷新頁面,我們發(fā)現(xiàn)問題能夠解決了。
2.Session和Cookie
接著我們再來重新看一下我們的項目還有哪些功能沒有實現(xiàn)呢?
1.我們打開登陸界面發(fā)現(xiàn),登陸界面有一個記錄用戶名選項,這個功能我們還沒有實現(xiàn)。
2.我們實現(xiàn)功能其實都是類似一個新聞類APP的后臺,這種頁面肯定需要做登陸判斷,所以我們還需要做登陸判斷。
3.有登陸判斷,就要實現(xiàn)退出登陸功能。
4.打開文章詳情頁,我們發(fā)現(xiàn)最近瀏覽這一行內(nèi)容沒有實現(xiàn),這里我們也需要實現(xiàn)一下。
在實現(xiàn)這四個功能之前老師要給你們介紹一個新的知識點,Session和Cookie,我們這四個功能都需要用到這四個功能。那么Session和Cookie又是什么呢?Session和Cookie作用在有些時候是一樣的,他們都是用來保存用戶數(shù)據(jù)的。但是他們的某些特性又非常的不同,導致他們的應用場景不同。接下來我們來詳細的了解一下這兩種技術(shù)。
Cookie
用來一定時間的保存用戶數(shù)據(jù),數(shù)據(jù)存儲在客戶端(網(wǎng)站的客戶端就是瀏覽器),啟用的時候能設置Cookie的有效時間,當時間截至的時候,Cookie失效.
Beego中對Cookie的存取刪
Beego把數(shù)據(jù)存儲到Cookie中代碼如下:
this.Ctx.SetCookie(key,value,time)//第一個參數(shù)是Cookie的key值,第二個參數(shù)是Cookie的value值,第三個參數(shù)是設置的Cookie的有效時間。
取Cookie的代碼如下:
this.Ctx.GetCookie(key)//參數(shù)是Cookie的key值,返回值是對應的value值。當沒有對應的Cookie或者Cookie已失效,返回空字符串
刪除Cookie的代碼如下:
this.Ctx.SetCookie(key,value,0)//第一個參數(shù)是Cookie的key值,第二個參數(shù)任意值,第三個參數(shù)把Cookie的值設置為小于0,就馬上失效。
Session
也是用來一定時間的保存用戶數(shù)據(jù),不過數(shù)據(jù)存儲在服務器,Beego啟用Sesssion的時候需要在配置文件中開啟Session功能。在Beego使用中,一般不設置Session的時間,當瀏覽器關(guān)閉的時候,Session失效。
**Beego中對Session的存取 **
如果想要在項目中使用Session功能,需要先在配置文件中設置Sessionon=true
Beego存儲Session的代碼:
this.SetSession(key,value)//兩個參數(shù),一個是Session的key,第二個是Session的Value
獲取Session的代碼如下:
this.GetSession(key)//參數(shù)是Session的key值,返回值是Session對應的value值,類型是interface{}
刪除Session的代碼如下:
this.DelSession(key)//參數(shù)是Session的key值
我們通過表格來分析他們的不同
不同點 | Cookie | Session |
---|---|---|
數(shù)據(jù)存儲位置 | 客戶端 | 服務器 |
數(shù)據(jù)安全性(相比較而言) | 低 | 高 |
生命周期 | 隨著設置時間的結(jié)束,生命周期結(jié)束 | 當瀏覽器關(guān)閉的時候,生命周期結(jié)束 |
適用場景 | 對安全性要求不高的,需要存儲時間較長的數(shù)據(jù) | 安全性要求搞,不需要長期存儲的數(shù)據(jù) |
簡單了解了這兩個知識點之后,我們來看一下,如何實現(xiàn)我們項目剩余的四個功能。
2.1記住用戶名
在登錄頁如果我們勾選了記住用戶名的選項框,在下次登陸的時候,用戶名那一欄就默認顯示上次存儲的用戶名。并且記住用戶名默認勾選,如果我們?nèi)∠催x記住用戶名,下次訪問登陸頁面的時候就不顯示用戶名,記住用戶名也不默認勾選。一般情況下,記住用戶名都能記住很久,對安全系數(shù)要求也不是很高,這里我們用Cookie來實現(xiàn)這個功能。
我們觀察視圖代碼發(fā)現(xiàn),當?shù)顷懙臅r候,form表單提交了記住用戶名單選框的數(shù)據(jù),用beego.Info()打印一下獲取到的數(shù)據(jù),發(fā)現(xiàn)當記住用戶名選中的時候我們在后臺會會獲取到字符串"on",沒有選中的時候獲取不到,根據(jù)這個現(xiàn)象,我們可以用來判斷是否鄧麗,當?shù)顷懙臅r候,我們可以用Cookie存儲用戶名,在沒有選中的時候刪除Cookie。代碼如下:
//處理注冊用戶名數(shù)據(jù)
//獲取數(shù)據(jù)
remember := this.GetString("remember")
beego.Info(remember)
if remember == "on"{beego.Info(remember)this.Ctx.SetCookie("userName",userName,1000)
}else {this.Ctx.SetCookie("userName",userName,-1)
}
在展示登陸頁面的時候,我們需要去獲取Cookie的值,然后判斷,如果獲取到了Cookie的值,就在用戶名里面顯示,并且把記住用戶名設置為選中狀態(tài),如果沒有獲取到Cookie的值就把用戶名設置為空,記住用戶名設置為非選中狀態(tài),代碼如下:
//獲取數(shù)據(jù)
userName := this.Ctx.GetCookie("userName")
//對數(shù)據(jù)進行判斷,然后設置數(shù)據(jù)傳遞給視圖
if userName != ""{this.Data["userName"] = userNamethis.Data["checked"] = "checked"
}else{this.Data["userName"] = ""this.Data["checked"] = ""
}
視圖中接收數(shù)據(jù):
<form class="login_form" name = "login" action="/login" method="post"><h1 class="login_title">用戶登錄</h1><input type="text" class="input_txt" name = "userName" value="{{.userName}}"><input type="password" name = "passwd" class="input_txt"><div class="remember"><input type="checkbox" name="remember" {{.checked}} ><label>記住用戶名</label></div><input type="submit" value="登 錄" class="input_sub">
</form>
注意,當checkbox添加一個checked屬性時,checkbox就為選中狀態(tài)
2.2登陸判斷
因為我們操作的都是后臺管理界面,所以我們需要做登陸判斷。我們這里面用Session來實現(xiàn)這個功能。
在使用Session之前記得要在配置文件中設置sessionon=true
當?shù)顷懗晒χ缶驮O置Session,代碼如下:
//設置session
this.SetSession("userName",userName)
后臺幾個展示頁面的函數(shù)都需要獲取session,然后判斷,代碼如下:
//獲取session,并判斷是否為空,如果為空跳轉(zhuǎn)到登錄頁面
userName := this.GetSession("userName")
if userName == nil{this.Redirect("/ShowLogin",302)return
}
2.3退出登陸
退出登錄其實就是刪除登陸session,然后跳轉(zhuǎn)回登陸界面。
-
在文章列表頁有個退出登陸,我們需要給他加一個href,這里我們規(guī)定退出登陸的請求路徑為
/logout
:<a href="/logout" class="logout fr">退 出</a>
-
接著我們在路由中指定請求對應的控制器和方法
beego.Router("/logout",&controllers.ArticleController{},"get:Logout")
-
然后我們實現(xiàn)一個Logout函數(shù),業(yè)務邏輯很簡單,我們直接看代碼
//退出登錄 func(this*ArticleController)Logout(){//刪除sessionthis.DelSession("userName")//跳轉(zhuǎn)this.Redirect("/login",302) }
2.4最近瀏覽
最近瀏覽也就是在我們?yōu)g覽文章的時候給文章添加上用戶信息,然后在再查詢這些信息,在頁面中顯示。
-
添加瀏覽信息
我們這里是給文章表添加瀏覽的用戶信息。代碼如下:
//獲取ORM對象 o := orm.NewOrm() //獲取插入數(shù)據(jù)的對象 var article models.Article article.Id = id o.Read(&article) //獲取多對多操作對象,用的是函數(shù)QueryM2M(),第一個參數(shù)是要插入數(shù)據(jù)的對象,第二個參數(shù)是要插入數(shù)據(jù)的字段名,返回值是多對多操作對象 m2m := o.QueryM2M(&article,"Users") //獲取要插入的對象 user := models.User{Name:userName.(string)} o.Read(&user,"Name") //多對多插入 m2m.Add(user)
-
顯示瀏覽信息
有兩種顯示多對多信息的方法
第一種,直接加載多對多關(guān)系,用的函數(shù)是LoadRelated(),第一個參數(shù)是查詢對象,第二個參數(shù)是多對多關(guān)系字段,代碼如下:
num,err := o.LoadRelated(&article,"Users")
這時候我們在前端就可以循環(huán)顯示最近瀏覽的用戶信息,這里我們用第二種視圖循環(huán)語法:
<label>最近瀏覽:</label> <p class="detail">{{range .article.Users}}{{.Name}} | {{end}}</p>
這時候我們多點幾次查看詳情會發(fā)現(xiàn)個問題,我們添加關(guān)系的時候是瀏覽一次就添加一次,那么我們顯示的時候就會重復顯示相同用戶的用戶名,效果如下:
但是我們一般瀏覽網(wǎng)頁的時候,一個用戶瀏覽過了只顯示一次該用戶信息即可,所以這里面我們需要去重,還記得我們前面介紹的高級查詢?nèi)ブ氐姆椒▎?#xff1f;Distinct()
去重,但是這個函數(shù)必須要是queryseter對象才能操作,所以我們第一種多對多查詢方法就不行了。這里我們用第二種多對多查詢。代碼如下:
var users []models.User
o.QueryTable("User").Filter("Articles__Article__Id",article.Id).Distinct().All(&users)
注意:我們這里插入的是想article中插入user,但是查詢的是從user中去獲取。
3.項目優(yōu)化
3.1路由過濾器
我們在項目實現(xiàn)的時候,只給文章列表頁和詳情頁添加了登陸判斷,我們思考一下,我們這個案例其實是整個的后臺管理,所以每個頁面都需要添加登陸判斷,那我們就需要每個地方都要添加登陸判斷,重復代碼很多。這里給大家介紹一個新的技術(shù),路由過濾器,在路由層面添加一個過濾,實現(xiàn)登陸判斷。那我們來看一下什么是路由過濾器。
作用:可以根據(jù)指定的匹配規(guī)則在特定的項目運行階段去執(zhí)行自定義函數(shù),函數(shù)一般放在beego.router()之前 。
那我們看一下路由過濾器函數(shù)的格式:
beego.InsertFilter("/article/*", position int, filter FilterFunc)
第一個參數(shù)是路由匹配規(guī)則,支持正則
第二個參數(shù)是指定項目運行階段,在beego項目運行過程中,框架幫我們分了五個階段,分別是:
a) BeforeStatic 靜態(tài)地址之前
b) BeforeRouter 尋找路由之前
c) BeforeExec 找到路由之后,開始執(zhí)行相應的 Controller 之前
d) AfterExec 執(zhí)行完 Controller 邏輯之后執(zhí)行的過濾器
e) FinishRouter 執(zhí)行完邏輯之后執(zhí)行的過濾器
具體對應是如下這種圖的時間點:
第三個參數(shù),就是指定過濾器函數(shù)。
路由過濾器一般放在beego.Router()之前。
那么我們接著來看一下過濾器函數(shù)的格式:
type FilterFunc func(*context.Context)
參數(shù)必須是context.Context
示例代碼:
var BeforeExecFunc = func(ctx * context.Context) {userName:=ctx.Input.Session("userName")if userName == nil{ctx.Redirect(302,"/login")}
}
beego.InsertFilter("/index",beego.BeforeExec,BeforeExecFunc)
3.2視圖布局
實現(xiàn)了過濾器函數(shù)之后,我們再來看我們整個項目,頁面顯示如下:
你會發(fā)現(xiàn)有些內(nèi)容在每個頁面中都有顯示,那我們能不能避免這些重復操作呢?這里給大家介紹一個新的知識點,視圖布局:
**作用:**通過設置模板頁面,其他頁面可以直接調(diào)用模板,避免再次處理重復代碼。
視圖布局本質(zhì)上就是兩個html界面的拼接,比如我們現(xiàn)在有一個包含重復部分的html界面layout.html,還有一個只包含添加文章業(yè)務的界面,我們可以根據(jù)如下去實現(xiàn)兩個頁面的拼接 。
操作如下:
控制器代碼如下:
this.Layout = "layout.html"
this.TplName = "add.html"
layout.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"><script type="text/javascript" src="/static/js/jquery-1.12.4.min.js"></script></head>
<body><div class="header"><a href="#" class="logo fl"><img src="/static/img/logo.png" alt="logo"></a><a href="/logout" 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="/addArticle" class="icon032">添加文章</a></li><li><a href="#" class="icon034">添加分類</a></li></ul></div>
</div>{{.LayoutContent}}</body>
</html>
注意這里面的 {{.LayoutContent}},這個標簽的地方就是用來存放add.html的地方。
add.html中就可以刪除掉相同的代碼,代碼如下:
<div class="main_body" id="main_body"><div class="breadcrub">當前位置:文章管理>添加文章</div><div class="pannel"><form method="post" action="/addArticle" enctype="multipart/form-data"><h3 class="review_title">添加文章</h3><div class="form_group"><label>文章標題:</label><input type="text" class="input_txt2" name="articleName" ></div><div class="form_group"><label>文章類型:</label><select class="sel_opt" name="select">{{range .articleTypes}}<option>{{.Tname}}</option>{{end}}</select></div><div class="form_group"><label>文章內(nèi)容:</label><textarea class="input_multxt" name="content"></textarea></div><div class="form_group"><label>上傳圖片:</label><input type="file" class="input_file" name="uploadname"></div><div class="form_group indent_group line_top"><input type="submit" value="添 加" class="confirm"><span>{{.errmsg}}</span></div></form></div>
</div>
在瀏覽器輸入網(wǎng)址,這時候你可能會發(fā)現(xiàn)問題,我們的
-
js代碼傳遞
細心的同學還會發(fā)現(xiàn),我們在某些頁面需要加js代碼,這個 內(nèi)容怎么傳遞到頁面當中呢,這里再給大家介紹一個功能LayoutSection。
**LayoutSection **作用:this.Layout指定了模板文件,可以實現(xiàn)兩個頁面的拼接,那有時候某些js或者是css樣式,該如何傳遞呢?我們可以用LayoutSection傳遞。
**LayoutSection **:用法:
控制器代碼:
this.Layout = " layout.html" this.LayoutSections = make(map[string]string) this.LayoutSections["Scripts"] = "scripts.html"
在layout.html中添加下面相應內(nèi)容:
<!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"><script type="text/javascript" src="/static/js/jquery-1.12.4.min.js"></script></head> <body><div class="header"><a href="#" class="logo fl"><img src="/static/img/logo.png" alt="logo"></a><a href="/logout" 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="/addArticle" class="icon032">添加文章</a></li><li><a href="#" class="icon034">添加分類</a></li></ul></div> </div>{{.LayoutContent}}</body> </html> {{.Scripts}}
3.3補充
我們回顧一下,看看我們的項目還有哪點沒有實現(xiàn)呢?類型的刪除是不是還沒有實現(xiàn),可能有的學生會說,老師這個刪除和文章的刪除一樣,直接刪除不久行了嘛!這里老師要特別提醒:**類型是與多表操作有關(guān)的,刪除效果和單表的文章不一樣 **
那我們來看一下類型的刪除:
同樣還是四步驟:**請求->路由->控制器->視圖 **
-
請求
刪除類型是在添加類型頁面中實現(xiàn)的,在這個頁面中有一個刪除的標簽,如下圖所示:
源碼路徑:https://gitee.com/cheng-penga/chengpeng-beego-go