当前位置:网站首页>GO語言開發天天生鮮項目第三天 案例-新聞發布系統二

GO語言開發天天生鮮項目第三天 案例-新聞發布系統二

2022-04-23 20:28:00 遊戲編程

1.類型相關內容

在實現類型相關業務之前,我們先創建類型錶。這裏我們添加上一對多多對多的關系。
一個類型下面有很多篇文章,但是一篇文章只屬於一個類型,所以文章與類型屬於一對多。
同時我們分析,一個用戶可以閱讀多篇文章,一篇文章也可以被多個用戶閱讀,所以文章和用戶之間屬於多對多關系。
由此,我們開始建錶,建錶代碼如下,我們根據代碼分析一對多,多對多如何設置:

type User struct {    Id int    Name string `orm:"unique"`    Passwd string `orm:"size(20)"`    Articles []*Article `orm:"rel(m2m)"` //設置多對多關系}//文章結構體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)"` //設置一對多關系    Users []*User `orm:"reverse(many)"`  //設置多對多的反向關系}//類型錶type ArticleType struct {    Id int    Tname string `orm:"size(20)"`    Articles []*Article `orm:"reverse(many)"` //設置一對多的反向關系}func init(){    //1.連接數據庫    orm.RegisterDataBase("default","mysql","root:[email protected](127.0.0.1:3306)/test?charset=utf8")    //2.注册錶    orm.RegisterModel(new(User),new(Article),new(ArticleType))    //3.生成錶    //1.數據庫別名    //2.是否强制更新    //3.創建錶過程是否可見    orm.RunSyncdb("default",false,true)}

根據我們以前學過數據庫知識,錶與錶之間有幾種關系?一般有三種,一對一,一對多,多對多,但是我們開發中常用的是一對多和多對多,這裏我們重點掌握這兩種,了解一對一即可。
orm中如何設置兩個錶之間的關系呢?
如果兩個錶之間有關系,ORM通過在兩個錶對應的結構體中添加對象指針或者對象指針數組來把兩個錶之間關聯起來,並且在對象指針和對象指針數組字段添加上相應的屬性,比如我們上面的文章錶和類型錶屬於一對多,就需要在文章結構體中添加一個類型的對象指針,然後設置一對多關系( orm:“rel(fk)” ),同樣的,在類型錶裏面需要有一個文章的對象指針數組,並且設置一對多的反向關系( orm:“reverse(many)” )。

  • **一對一 ** 關系設置:兩個對應的結構體中都添加對方的結構體指針,然後設置一對一關系( orm:“rel(one)” ),反向關系設置為 orm:“rel(one)”

  • **一對多 ** 關系設置:一對多中兩錶之間的關系不可互換,以文章錶和類型錶為例,當創建錶的時候 在 文章錶對應的文章結構體中添加類型錶的對象指針,並且設置一對多關系( orm:“rel(fk)” ), 在 類型張錶對應的結構體中添加文章錶的對象指針數組,並且設置一對多的反向關系( orm:“reverse(many)” ) 生成錶的時候,數據庫會自動在 文章錶中添加類型錶的錶的Id作為文章錶的外鍵。如圖:

    GO語言開發天天生鮮項目第三天 案例-新聞發布系統二 - 第1張

    一對多插入操作 :只需要在文章錶插入類型對象即可。代碼如下:

o := orm.NewOrm()article := models.Article{}artiType := models.ArticleType{Id:id}o.Read(&artiType)article.ArticleType = &artiTypeo.Insert(&article)

一對多查詢: ORM做多錶查詢的時候默認是惰性查詢,即不明確指出來要做多錶查詢,即便是兩個錶之間存在關系,ORM也不會給兩個錶做關聯。指定多錶查詢的函數是RelatedSel()。參數是要關聯的錶名,可以有多個。代碼如下:

count,err = o.QueryTable("Article").RelatedSel("ArticleType").Count()

如果關聯錶的那個字段沒有值,那麼數據查不到

  • 多對多 關系設置:多對多中兩錶之間的關系是平等的,所以他們的屬性設置可以呼喚,以文章錶和用戶錶為例,當創建錶的時候 在 文章錶對應的文章結構體中添加用戶錶的對象指針數組,並且設置多對多關系( orm:“rel(m2m)” ), 在用戶錶對應的結構體中添加文章錶的對象指針數組,並且設置多對多的反向關系( orm:“reverse(many)” ) 生成錶的時候,數據庫會生成一個用戶和文章之間的關系錶,有三個字段,Id,用戶錶Id,文章錶ID。如下圖:
    GO語言開發天天生鮮項目第三天 案例-新聞發布系統二 - 第2張

    多對多插入操作

o := orm.NewOrm()//1.獲取操作對象arti:= Article{Id: 1}//獲取article的多對多操作對象m2m := o.QueryM2M(&arti, "users")//第一個參數對象必須有主鍵,第二個參數是字段名//獲取要插入的對象user := &User{Id:1}o.Read(&user)//多對多對象插入num, err := m2m.Add(user)//參數可以為對象,指針,對象數組,指針數組

多對多查詢: 有兩種方法: 第一種:直接用read查詢,然後加上 LoadRelated ()函數來關聯兩張錶。代碼如下:

post := Post{Id: 1}err := o.Read(&post)num, err := o.LoadRelated(&post, "Tags")

優點是簡單,快捷。 缺點是返回值不是queryseter,不能調用其他的高級查詢。 第二種方法,是通過過濾器查詢,指定錶之後,用Filter()過濾相應的條件,第一個參數是 錶示另一張錶的字段__另外一張錶的錶名__比較的字段 (注意是雙下劃線),第二個字段是要比較的值,需要注意的是這個順序是和錶的插入順序相反的。代碼如下:

1.1添加類型

分析過多錶之間的操作之後,我們來實現類型有關的業務,首先我們需要先添加類型。

1.1.1添加類型頁面顯示

  • 確定添加類型顯示的請求路徑為 /AddArticleType

  • 在路由文件中添加相關代碼。

beego.Router("/addArticleType",&controllers.ArticleController{},"get:ShowAddType")
  • 然後去控制器中實現ShowAddType函數,先簡單的指定視圖。代碼如下:
//展示添加文章類型頁面func(this*ArticleController)ShowAddType(){    this.TplName = "addType.html"}
  • 然後在瀏覽器輸入請求http://192.168.110.74:8080/addArticleType,頁面顯示如下:
    GO語言開發天天生鮮項目第三天 案例-新聞發布系統二 - 第3張

    由頁面可知,我們添加文章類型界面,分兩塊,一塊是上面以錶格的形式顯示所有類型,一塊是下面增加分類。我們先來處理增加分類。

1.1.2添加類型數據處理

添加類型業務比較簡單,首先是修改我們的視圖頁面內容,給form標簽請求方式和請求路徑,代碼如下:

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

接著我們要修改路由文件,給請求指定控制器,指定方法:

beego.Router("/addArticleType",&controllers.ArticleController{},"get:ShowAddType;post:HandleAddType")

然後我們實現一下後臺處理函數,這個函數的實現步驟和以前實現添加文章的步驟一樣,代碼處理還更簡單,不詳細分析,我們直接看代碼:

//處理添加文章類型數據func(this*ArticleController)HandleAddType(){    //獲取數據    typeName := this.GetString("typeName")    //數據校驗    if typeName == ""{        beego.Info("添加數據失敗")        return    }    //插入數據庫    o := orm.NewOrm()    var articleType models.ArticleType    articleType.Tname = typeName    if _,err :=o.Insert(&articleType);err != nil{        beego.Info("添加數據失敗")        return    }    //返回視圖    this.TplName = "addType.html"}

這裏我們用渲染的方式返回視圖合適不合適,思考一下!

1.1.3查詢類型數據

現在我們類型錶有數據了,可以在顯示頁面的時候把數據填充在頁面上

  • 後臺代碼
//展示添加文章類型頁面func(this*ArticleController)ShowAddType(){    //查詢數據    o := orm.NewOrm()    var articleTypes []models.ArticleType    o.QueryTable("ArticleType").All(&articleTypes)    //傳遞數據給視圖並指定視圖    this.Data["articleTypes"] = articleTypes    this.TplName = "addType.html"}
  • 視圖代碼 在視圖頁面中,我們循環控制器傳遞過來的數組,拿到我們需要的數據
{{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 ,得到如下頁面:

GO語言開發天天生鮮項目第三天 案例-新聞發布系統二 - 第5張

添加一個類型測試,然後發現頁面還是沒有類型顯示,這個說明我們代碼處理出問題了,哪裏出問題了呢?還記得前面給大家留的思考題嗎?我們添加完文章類型之後,是直接渲染加載了視圖,這時候並沒有給視圖傳遞數據,所以也就沒有類型顯示。這樣的結果和我們的業務 不符合,所以我們需要把添加完類型之後跳轉頁面的方式改為重定向,然後再看結果,發現類型顯示正常。

1.2首頁根據下拉框選項不同,獲取不同類型數據

現在有類型數據了,我們添加文章的時候也需要添加上類型了。

1.2.1添加帶類型的文章

  • 在展示頁面的時候需要把類型數據綁定添加類型的下拉框 後臺獲取數據(在展示添加文章界面那個函數裏面寫相關代碼)
//展示添加文章界面func (this*ArticleController)ShowAddArticle(){    //查詢數據    o := orm.NewOrm()    var articleTypes []models.ArticleType    o.QueryTable("ArticleType").All(&articleTypes)    //傳遞數據給視圖並指定視圖    this.Data["articleTypes"] = articleTypes    this.TplName = "add.html"}

視圖展示數據 循環獲取數據,在下拉框中顯示類型名稱

<select class="sel_opt" name="select">     {{range .articleTypes}}            <option>{{.Tname}}</option>     {{end}}</select>
  • 添加文章的時候指定文章類型,代碼如下:
//給文章對象指定文章類型    var articleType models.ArticleType    articleType.Tname = typeName    o.Read(&articleType,"Tname")    article.ArticleType = &articleType    //插入    o.Insert(&article)

1.2.2列錶頁展示文章時,展示類型信息。

  • 查詢所有問章,關聯文章類型錶(查詢的時候加上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}}

這時候你發現,以前添加的文章都沒有顯示,還記得我們前面介紹多錶操作的時候介紹的嗎,加上RelatedSel之後,如果相應的字段沒有數據,將查詢不出來。

1.2.3根據下拉框選項不同,獲取不同類型數據

  • 查詢類型數據,並把數據綁定到下拉框 這個業務代碼和添加文章的業務代碼一樣,我們就不做詳細分析,直接看代碼:
//查詢數據    var articleTypes []models.ArticleType    o.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>
  • 根據下拉框選中類型,獲取相同類型的文章 把選中的類型數據傳遞給後臺 我們以前傳遞數據是用form錶單,這裏我們還是用form錶單把下拉框包起來,然後把選中的數據傳遞給後臺。代碼如下:
<form method="get" action="/ShowArticleList">     <select name="select" id="select" class="sel_opt">          {{range .articleTypes}}               <option selected="true">{{.Tname}}</option>          {{end}}     </select></form>

思考,我們為什麼用get請求不用post請求

這裏沒有發送請求按鈕(盡量不要改美工設計的頁面),我們通過js代碼發送請求,js代碼如下:

$("#select").change(function () {     $("#form").submit()})

根據獲取的類型,查詢有多少條數據,以及顯示相同類型的文章 獲取前端傳遞過來的數據

//獲取類型名稱    typeName := this.GetString("select")

根據類型,查詢有多少條符合條件的數據,但是,需要注意這裏面要考慮沒有傳遞類型名稱的請求,所以需要做個判斷,代碼如下:

//獲取類型名稱typeName := this.GetString("select")//查詢數據,以及分頁顯示o := orm.NewOrm()qs := o.QueryTable("Article")var count int64//數據校驗if typeName == ""{    count,_ =qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Count()}else {    count,_ =qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Count()}

其他處理分頁的業務代碼不變,代碼如下:

//確定每頁顯示數pageSize := 2//獲取總頁數pageCount :=math.Ceil(float64(count) / float64(pageSize))//獲取頁碼pageIndex,err := this.GetInt("pageIndex")if err != nil{    pageIndex = 1}//確定數據的起始比特置start := (pageIndex - 1) * pageSize

根據類型查詢相同類型的數據,同樣需要做一個判斷。代碼如下:

//查詢相應類型的數據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)}

其他代碼不變,獲取列錶頁完整代碼如下:

func(this*ArticleController)ShowArticleList(){    //獲取類型名稱    typeName := this.GetString("select")    //查詢數據,以及分頁顯示    o := orm.NewOrm()    qs := o.QueryTable("Article")    var count int64    //數據校驗s    if typeName == ""{        count,_ =qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Count()    }else {        count,_ =qs.RelatedSel("ArticleType").Filter("ArticleType__Tname",typeName).Count()    }    //確定每頁顯示數    pageSize := 2    //獲取總頁數    pageCount :=math.Ceil(float64(count) / float64(pageSize))    //獲取頁碼    pageIndex,err := this.GetInt("pageIndex")    if err != nil{        pageIndex = 1    }    //確定數據的起始比特置    start := (pageIndex - 1) * pageSize    //查詢相應類型的數據    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)    }    //查詢數據庫部分數據    //獲取類型數據    //查詢數據    var articleTypes []models.ArticleType    o.QueryTable("ArticleType").All(&articleTypes)    this.Data["articleTypes"] = articleTypes    this.Data["count"] = count    this.Data["pageCount"] = int(pageCount)    this.Data["pageIndex"] = pageIndex    //傳遞數據並指定視圖    this.Data["articles"] = articles    this.TplName = "index.html"}

這時候你再看頁面,會發現一個問題,有一個選項一直都不能够選中。為什麼呢?

是因為,我們每一次改變下拉框的選項,都會讓js發出get請求給後臺,後臺就會重新查詢所有的類型錶綁定下拉框,所以每次顯示的都是一個數據,這樣的話,我們選中顯示的那條數據,就無法觸發js發送請求,因為js認為下拉框顯示內容並沒有變化。這時候下拉框顯示也有問題,那怎麼解决這個問題呢?

1.2.4解决下拉框選項顯示的問題

​ 通過前面的分析,我們知道每次下拉框都是重新從數據庫中獲取類型數據進行綁定,這裏面我們就需要對選中的類型加一個判斷,當從數據庫中取出的數據是選中的類型時,就給下拉框選項屬性selected設置為true。首先後臺要傳遞當前選中的類型名稱給視圖,代碼如下:

//傳遞當下拉框選擇的類型名給視圖this.Data["typeName"] = typeName
  • 前端代碼處理 視圖中我們接收控制器傳遞過來的當前選中類型,然後與數據庫中的類型名進行比較,如果相同就設置選中不同就不設置,代碼如下:
<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>

需要注意的是,如果是在循環中獲取控制器傳遞過來的數據,不能直接用 . ,要用 $.

然後刷新頁面,我們發現問題能够解决了。

2.Session和Cookie

接著我們再來重新看一下我們的項目還有哪些功能沒有實現呢?1.我們打開登陸界面發現,登陸界面有一個記錄用戶名選項,這個功能我們還沒有實現。2.我們實現功能其實都是類似一個新聞類APP的後臺,這種頁面肯定需要做登陸判斷,所以我們還需要做登陸判斷。3.有登陸判斷,就要實現退出登陸功能。4.打開文章詳情頁,我們發現最近瀏覽這一行內容沒有實現,這裏我們也需要實現一下。
在實現這四個功能之前老師要給你們介紹一個新的知識點,Session和Cookie,我們這四個功能都需要用到這四個功能。那麼Session和Cookie又是什麼呢?Session和Cookie作用在有些時候是一樣的,他們都是用來保存用戶數據的。但是他們的某些特性又非常的不同,導致他們的應用場景不同。接下來我們來詳細的了解一下這兩種技術。
Cookie
用來一定時間的保存用戶數據,數據存儲在客戶端(網站的客戶端就是瀏覽器),啟用的時候能設置Cookie的有效時間,當時間截至的時候,Cookie失效.
Beego中對Cookie的存取删
Beego把數據存儲到Cookie中代碼如下:

this.Ctx.SetCookie(key,value,time)//第一個參數是Cookie的key值,第二個參數是Cookie的value值,第三個參數是設置的Cookie的有效時間。

取Cookie的代碼如下:

this.Ctx.GetCookie(key)//參數是Cookie的key值,返回值是對應的value值。當沒有對應的Cookie或者Cookie已失效,返回空字符串

删除Cookie的代碼如下:

this.Ctx.SetCookie(key,value,0)//第一個參數是Cookie的key值,第二個參數任意值,第三個參數把Cookie的值設置為小於0,就馬上失效。

Session
也是用來一定時間的保存用戶數據,不過數據存儲在服務器,Beego啟用Sesssion的時候需要在配置文件中開啟Session功能。在Beego使用中,一般不設置Session的時間,當瀏覽器關閉的時候,Session失效。
**Beego中對Session的存取 **
如果想要在項目中使用Session功能,需要先在配置文件中設置Sessionon=true
Beego存儲Session的代碼:

this.SetSession(key,value)//兩個參數,一個是Session的key,第二個是Session的Value

獲取Session的代碼如下:

this.GetSession(key)//參數是Session的key值,返回值是Session對應的value值,類型是interface{}

删除Session的代碼如下:

this.DelSession(key)//參數是Session的key值

我們通過錶格來分析他們的不同

不同點 Cookie Session
數據存儲比特置 客戶端 服務器
數據安全性(相比較而言)
生命周期 隨著設置時間的結束,生命周期結束 當瀏覽器關閉的時候,生命周期結束
適用場景 對安全性要求不高的,需要存儲時間較長的數據 安全性要求搞,不需要長期存儲的數據

簡單了解了這兩個知識點之後,我們來看一下,如何實現我們項目剩餘的四個功能。

2.1記住用戶名

在登錄頁如果我們勾選了記住用戶名的選項框,在下次登陸的時候,用戶名那一欄就默認顯示上次存儲的用戶名。並且記住用戶名默認勾選,如果我們取消勾選記住用戶名,下次訪問登陸頁面的時候就不顯示用戶名,記住用戶名也不默認勾選。一般情况下,記住用戶名都能記住很久,對安全系數要求也不是很高,這裏我們用Cookie來實現這個功能。
我們觀察視圖代碼發現,當登陸的時候,form錶單提交了記住用戶名單選框的數據,用beego.Info()打印一下獲取到的數據,發現當記住用戶名選中的時候我們在後臺會會獲取到字符串"on",沒有選中的時候獲取不到,根據這個現象,我們可以用來判斷是否鄧麗,當登陸的時候,我們可以用Cookie存儲用戶名,在沒有選中的時候删除Cookie。代碼如下:

//處理注册用戶名數據//獲取數據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的值,就在用戶名裏面顯示,並且把記住用戶名設置為選中狀態,如果沒有獲取到Cookie的值就把用戶名設置為空,記住用戶名設置為非選中狀態,代碼如下:

//獲取數據userName := this.Ctx.GetCookie("userName")//對數據進行判斷,然後設置數據傳遞給視圖if userName != ""{    this.Data["userName"] = userName    this.Data["checked"] = "checked"}else{    this.Data["userName"] = ""    this.Data["checked"] = ""}

視圖中接收數據:

<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就為選中狀態

2.2登陸判斷

因為我們操作的都是後臺管理界面,所以我們需要做登陸判斷。我們這裏面用Session來實現這個功能。
在使用Session之前記得要在配置文件中設置sessionon=true
當登陸成功之後就設置Session,代碼如下:

//設置sessionthis.SetSession("userName",userName)

後臺幾個展示頁面的函數都需要獲取session,然後判斷,代碼如下:

//獲取session,並判斷是否為空,如果為空跳轉到登錄頁面userName := this.GetSession("userName")if userName == nil{    this.Redirect("/ShowLogin",302)    return}

2.3退出登陸

退出登錄其實就是删除登陸session,然後跳轉回登陸界面。

  • 在文章列錶頁有個退出登陸,我們需要給他加一個href,這裏我們規定退出登陸的請求路徑為 /logout
<a href="/logout" class="logout fr">退 出</a>
  • 接著我們在路由中指定請求對應的控制器和方法
beego.Router("/logout",&controllers.ArticleController{},"get:Logout")
  • 然後我們實現一個Logout函數,業務邏輯很簡單,我們直接看代碼
//退出登錄func(this*ArticleController)Logout(){    //删除session    this.DelSession("userName")    //跳轉    this.Redirect("/login",302)}

2.4最近瀏覽

最近瀏覽也就是在我們瀏覽文章的時候給文章添加上用戶信息,然後在再查詢這些信息,在頁面中顯示。

  • 添加瀏覽信息 我們這裏是給文章錶添加瀏覽的用戶信息。代碼如下:
//獲取ORM對象o := orm.NewOrm()//獲取插入數據的對象var article models.Articlearticle.Id = ido.Read(&article)//獲取多對多操作對象,用的是函數QueryM2M(),第一個參數是要插入數據的對象,第二個參數是要插入數據的字段名,返回值是多對多操作對象m2m := o.QueryM2M(&article,"Users")//獲取要插入的對象user := models.User{Name:userName.(string)}o.Read(&user,"Name")//多對多插入m2m.Add(user)
  • 顯示瀏覽信息 有兩種顯示多對多信息的方法 第一種,直接加載多對多關系,用的函數是LoadRelated(),第一個參數是查詢對象,第二個參數是多對多關系字段,代碼如下:
num,err := o.LoadRelated(&article,"Users")

這時候我們在前端就可以循環顯示最近瀏覽的用戶信息,這裏我們用第二種視圖循環語法:

<label>最近瀏覽:</label><p class="detail">{{range .article.Users}}{{.Name}} | {{end}}</p>

這時候我們多點幾次查看詳情會發現個問題,我們添加關系的時候是瀏覽一次就添加一次,那麼我們顯示的時候就會重複顯示相同用戶的用戶名,效果如下:

GO語言開發天天生鮮項目第三天 案例-新聞發布系統二 - 第6張

但是我們一般瀏覽網頁的時候,一個用戶瀏覽過了只顯示一次該用戶信息即可,所以這裏面我們需要去重,還記得我們前面介紹的高級查詢去重的方法嗎? Distinct() 去重,但是這個函數必須要是queryseter對象才能操作,所以我們第一種多對多查詢方法就不行了。這裏我們用第二種多對多查詢。代碼如下:

var users []models.Usero.QueryTable("User").Filter("Articles__Article__Id",article.Id).Distinct().All(&users)

注意:我們這裏插入的是想article中插入user,但是查詢的是從user中去獲取。

3.項目優化

3.1路由過濾器

我們在項目實現的時候,只給文章列錶頁和詳情頁添加了登陸判斷,我們思考一下,我們這個案例其實是整個的後臺管理,所以每個頁面都需要添加登陸判斷,那我們就需要每個地方都要添加登陸判斷,重複代碼很多。這裏給大家介紹一個新的技術,路由過濾器,在路由層面添加一個過濾,實現登陸判斷。那我們來看一下什麼是路由過濾器。
作用:可以根據指定的匹配規則特定的項目運行階段執行自定義函數 ,函數一般放在beego.router()之前 。
那我們看一下路由過濾器函數的格式:

beego.InsertFilter(pattern string, position int, filter FilterFunc)

第一個參數是路由匹配規則,支持正則
第二個參數是指定項目運行階段,在beego項目運行過程中,框架幫我們分了五個階段,分別是:
a) BeforeStatic 靜態地址之前
b) BeforeRouter 尋找路由之前
c) BeforeExec 找到路由之後,開始執行相應的 Controller 之前
d) AfterExec 執行完 Controller 邏輯之後執行的過濾器
e) FinishRouter 執行完邏輯之後執行的過濾器
具體對應是如下這種圖的時間點:

GO語言開發天天生鮮項目第三天 案例-新聞發布系統二 - 第7張

第三個參數,就是指定過濾器函數。

路由過濾器一般放在beego.Router()之前。

那麼我們接著來看一下過濾器函數的格式:

type FilterFunc func(*context.Context)

參數必須是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視圖布局

實現了過濾器函數之後,我們再來看我們整個項目,頁面顯示如下:

GO語言開發天天生鮮項目第三天 案例-新聞發布系統二 - 第8張

你會發現有些內容在每個頁面中都有顯示,那我們能不能避免這些重複操作呢?這裏給大家介紹一個新的知識點,視圖布局:
**作用:**通過設置模板頁面,其他頁面可以直接調用模板,避免再次處理重複代碼。
視圖布局本質上就是兩個html界面的拼接,比如我們現在有一個包含重複部分的html界面layout.html,還有一個只包含添加文章業務的界面,我們可以根據如下去實現兩個頁面的拼接 。
操作如下:
控制器代碼如下:

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>文章內容:</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>

在瀏覽器輸入網址,這時候你可能會發現問題,我們的標簽 這種小部分沒辦法改變。這裏我們可以通過this.Data給layout傳值。

  • js代碼傳遞 細心的同學還會發現,我們在某些頁面需要加js代碼,這個 內容怎麼傳遞到頁面當中呢,這裏再給大家介紹一個功能LayoutSection。 **LayoutSection **作用:this.Layout指定了模板文件,可以實現兩個頁面的拼接,那有時候某些js或者是css樣式,該如何傳遞呢?我們可以用LayoutSection傳遞。 **LayoutSection **:用法: 控制器代碼:
this.Layout = " layout.html"this.LayoutSections = make(map[string]string)this.LayoutSections["Scripts"] = "scripts.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>{{.Scripts}}

3.3補充

我們回顧一下,看看我們的項目還有哪點沒有實現呢?類型的删除是不是還沒有實現,可能有的學生會說,老師這個删除和文章的删除一樣,直接删除不久行了嘛!這裏老師要特別提醒:**類型是與多錶操作有關的,删除效果和單錶的文章不一樣 **
那我們來看一下類型的删除:
同樣還是四步驟:**請求->路由->控制器->視圖 **

  • 請求 删除類型是在添加類型頁面中實現的,在這個頁面中有一個删除的標簽,如下圖所示:
    GO語言開發天天生鮮項目第三天 案例-新聞發布系統二 - 第4張

    那我們給這個標簽加上請求路徑,同樣的,我們需要給請求路徑上加上類型Id。代碼如下:

<a href="/deleteType?id={{.Id}}" class="edit">删除</a>
  • 路由 添加相應路由,指定控制器和方法
beego.Router("/deleteType",&controllers.ArticleController{},"get:DeleteType")
  • 控制器 有了方法名,就實現相關代碼:
//删除類型func(this*ArticleController)DeleteType(){    //獲取數據    id,err:=this.GetInt("id")    //校驗數據    if err != nil{        beego.Info(err)        return    }    //處理數據    var articleType models.ArticleType    articleType.Id = id    o := orm.NewOrm()    o.Delete(&articleType)    //返回視圖    this.Redirect("/addArticleType",302)}
  • 視圖 删除之後,我們返回頁面發現,類型確實删除了。但是需要注意的是,我們類型綁定的還有相關的文章,這時候我們回到文章列錶頁,發現, 删除類型,把該類型有關的文章也删除了 ,這是因為,beego默認執行的是級聯删除,那這個級聯删除能不能設置呢?在beego中級聯删除的設置,是在建錶的時候添加的設置如下: 設置對應的 rel 關系删除時,如何處理關系字段。
cascade        級聯删除(默認值)set_null       設置為 NULL,需要設置 null = trueset_default    設置為默認值,需要設置 default 值do_nothing     什麼也不做,忽略

示例:

//文章結構體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);null;on_delete(set_null)"`    Users []*User `orm:"reverse(many)"`}

4.Beego總結

err:=this.GetInt(“id”)
//校驗數據
if err != nil{
beego.Info(err)
return
}
//處理數據
var articleType models.ArticleType
articleType.Id = id
o := orm.NewOrm()
o.Delete(&articleType)
//返回視圖
this.Redirect(“/addArticleType”,302)
}

+ 視圖删除之後,我們返回頁面發現,類型確實删除了。但是需要注意的是,我們類型綁定的還有相關的文章,這時候我們回到文章列錶頁,發現,**删除類型,把該類型有關的文章也删除了**,這是因為,beego默認執行的是級聯删除,那這個級聯删除能不能設置呢?在beego中級聯删除的設置,是在建錶的時候添加的設置如下:設置對應的 rel 關系删除時,如何處理關系字段。 

設置級聯屬性
cascade 級聯删除(默認值)
set_null 設置為 NULL,需要設置 null = true
set_default 設置為默認值,需要設置 default 值
do_nothing 什麼也不做,忽略

 示例:```go//文章結構體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);null;on_delete(set_null)"`    Users []*User `orm:"reverse(many)"`}

4.Beego總結

大總結,我們在課堂上進行,也可以自行先總結一下
作者:辦公模板庫 素材蛙

遊戲編程 ️,一個遊戲開發收藏夾~

如果圖片長時間未顯示,請使用Chrome內核瀏覽器。

版权声明
本文为[遊戲編程]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204232027530795.html