当前位置: 首页 > news >正文

网站制作一薇深圳网站订制开发

网站制作一薇,深圳网站订制开发,app网站设计制作,天津建设工程投标信息Beego案例-新闻发布系统 1.注册 后台代码和昨天案例代码一致。,所以这里面只写一个注册的业务流程图。 **业务流程图 ** 2.登陆 业务流程图 登陆和注册业务和我们昨天登陆和注册基本一样#xff0c;所以就不再重复写这个代码 但是我们遇到的问题是如何做代码的迁移所以就不再重复写这个代码 但是我们遇到的问题是如何做代码的迁移把昨天的登陆和注册拿过来直接用 首先我们需要把静态页面拷贝到我们项目目录下面。 进入项目目录删除掉原来的static文件夹 然后拷贝我们昨天课堂资料中的 static.zip到这个目录并解压解压之后如下图 打开static文件夹显示如下则说明拷贝成功 把static文件夹中所有的html文件都拷贝到views文件夹下面昨天的几个页面已经没用了可以删除拷贝之后views文件显示如下 这时候用GoLand打开我们的项目,显示如下 打开register.html页面修改页面中form表单的内容 给form标签加上action/register methodpost属性 给两个input标签的name分别改为nameuserName和name passwd 表单相关代码如下: form classlogin_form name login action/register methodposth1 classlogin_title用户注册/h1input typetext placeholder用户名 classinput_txt nameuserNameinput typepassword placeholder密码 classinput_txt name passwdinput typesubmit value注 册 classinput_sub /form打开login.html页面修改form表单的内容 给form标签加上action/login methodpost属性 给两个input标签的name分别改为nameuserName和name passwd 表单相关代码如下 form classlogin_form name login action/login methodposth1 classlogin_title用户登录/h1input typetext classinput_txt name userNameinput typepassword name passwd classinput_txtdiv classrememberinput typecheckbox nameremember label记住用户名/label/divinput typesubmit value登 录 classinput_sub /form登陆界面多了个记住用户名标签明天我们实现这个功能 改完之后运行项目测试注册和登陆页面能够能唱显示并且功能没有问题说明代码迁移成功。 3.创建数据库 3.1数据库表的设计 接下来我们就要实现文章相关的操作所以这里我们要在数据库中生成一个文章表。 我们以前在数据库中创建表的时候会给字段加很多限制属性比如非空长度默认值等等在ORM中创建表时也可以给各个字段添加相应的限制。那如何去加限制呢我们先看例子 type Article struct {Id int orm:pk;autoArtiName string orm:size(20)Atime time.Time orm:auto_nowAcount int orm:default(0);nullAcontent 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下方查看创建表过程也可以进入数据库查看是否建表成功成功的话数据库显示如下: 登陆成功之后访问新闻列表展示页面但是我们现在还没有新闻所以我们先实现插入文章界面。 添加文字显示实现 修改路由文件 在router.go文件的init函数中添加下面这一行代码 //文章列表页访问 beego.Router(/showArticleList, controllers.ArticleController{}, get:ShowArticleList)显示文章界面的显示 先创建一个article.go文件用来存放文章有关的业务代码 然后在article.go文件中创建一个ArticleController控制器并定义一个ShowArticleList函数代码如下: package controllersimport beego github.com/beego/beego/v2/server/webtype ArticleController struct {beego.Controller }func (this *ArticleController) ShowArticleList() {this.TplName index.html }写完代码之后我们从浏览器登录以后跳转http://localhost:8080/showArticleList如果能在浏览器中看到下面这个界面表示页面展示成功 4.插入文章 业务流程图 插入页面我们用的视图是add.html这里我们规定添加文章界面的请求路径为/addArticle 4.1修改路由文件 在router.go文件的init函数中添加下面这一行代码 beego.Router(/addArticle,controllers.ArticleControlle{},get:ShowAddArticle)写完代码之后我们从浏览器发出一个请求localhost:8080/addArticle如果能在浏览器中看到下面这个界面表示页面展示成功 4.2添加文章界面的显示 先创建一个article.go文件用来存放文章有关的业务代码 然后在article.go文件中创建一个ArticleController控制器并定义一个ShowAddArticle函数代码如下: import github.com/astaxie/beegotype ArticleController struct {beego.Controller }func (this*ArticleController)ShowAddArticle(){}接着我们来实现ShowAddArticle函数这个函数只是用来展示页面的所以我们只需要给他制定一个视图就可以代码如下: func (this*ArticleController)ShowAddArticle(){this.TplName add.html }写完代码之后我们从浏览器发出一个请求localhost:8080/addArticle如果能在浏览器中看到下面这个界面表示页面展示成功 4.3插入文章数据处理 上面我们显示了添加文章界面观察界面可以发现我们需要获取文章标题文章类型, 文章内容上传图片。其中文章类型牵涉到多表操作暂时只做简单的单表操作。首先让我们来看一下插入页面的前端部分修改。 4.3.1前端页面修改 由页面可知我们这里面是要上传数据所以我们这里需要一个form表单打开前端界面add.html能看到我们这里面确实有一个标签只是没有属性我们需要给标签添加action和method属性这个请求还是添加文章所以我们还可以用添加文章的请求路径设置action属性action/addArticle。因为上传数据我们这里用post方法设置method属性methodpost。其他部分不用修改。form修改代码如下 form methodpost action/addArticle4.3.2路由内容修改 我们在前端添加了addArticle请求的post方法所以需要修改一下router.go给addArticle的post请求指定一个函数修改代码如下: beego.Router(/addArticle,controllers.ArticleController{},get:ShowAddArticle;post:HandleAddArticle)4.3.3后台代码实现 有了函数名之后我们就需要在后台中实现这个函数。 首先是获取数据 这时候我们看一下前端界面我们需要获取文章标题 文章内容上传图片数据文章标题和文章内容都是字符串比较简单直接通过GetString获取所以我们先获取这两个内容。通过查看add.html代码我们发现文章标题对应的标签name等于articleName文章内容对应的标签name等于content注意这里用的是textarea标签不是用的input但是获取数据方式一样。获取数据的代码如下 //获取数据 articleName : this.GetString(articleName) content : this.GetString(content)获取数据之后就做数据校验我们这里还是做判空校验 //对数据进行校验 if articleName || content {beego.Info(添加文章数据不完整)this.TplName add.htmlreturn }正常的添加流程在校验完数据之后就要把数据插入数据库了但是我们添加文章这个界面有点特殊因为这里面牵涉到一个静态文件的上传所以我们先处理静态文件上传功能。 静态文件上传(难点) 前端代码 如果form表单中牵涉到文件上传在form表单中就需要添加一个属性enctypemultipart/form-data不然上传就是假上传后台不能获取到上传的文件。修改如下 form methodpost action/addArticle enctypemultipart/form-datainput typefile classinput_file nameuploadname后台代码修改 后台接收上传文件有两个函数可以用。 GetFile(key string) (multipart.File, *multipart.FileHeader, error) 作用: 是获取前端传递过来的文件。 参数: 是input标签中的name值 返回值: 有三个一个是文件流就是我们打开文件返回的内容第二个是文件相关信息包括文件头文件大小文件名字等第三个是错误信息。示例代码如下 file,head,err : this.GetFile(uploadname)if err ! nil{beego.Info(上传图片错误请重新添加)this.TplName add.htmlreturn}SaveToFile(fromfile, tofile string) error 作用:直接保存前端出过来的文件。 参数: 有两个参数第一个参数是前端标签的name属性值第二个参数是文件在服务器端存储的位置。**注意:这个位置字符串在前面需要加一个. 返回值:是错误信息。示例代码如下 err : this.SaveToFile(uploadname,./static/img/1.jpg) if err ! nil{beego.Info(上传图片错误请重新添加)this.TplName add.htmlreturn}在我们开发过程中如果后台接收文件并存储需要做以下几种判断 文件格式判断 我们通过GetFile可以获取到文件名然后通过path包可以分离出文件的后缀即文件格式把你需要的文件格式过滤出来不需要的返回即可。我们根据文件名获取文件后缀代码如下: //文件格式判断fileExt : path.Ext(head.Filename)if fileExt ! .jpg fileExt ! .png fileExt ! .jpeg{beego.Info(上传图片格式不正确请重新添加)this.TplName add.htmlreturn}文件大小的判断 我们获取文件之后在存储之前文件流一般是在内存中所以文件不易过大我们在这里做一个文件大小的判断。代码如下: //文件大小判断if head.Size 5000000{beego.Info(上传图片太大请重新添加)this.TplName add.htmlreturn}避免文件重名 获取文件之后我们要把文件存储到服务器上但是用户可能会上传同名的文件如果文件同名的话后来上传的文件就把之前上传的文件给覆盖了所以我们要给上传的文件重新确定一个名字。这里我们以上传文件时的时间作为上传文件的文件名。默认的时间格式和我们常见的时间格式不一样所以这里我们需要对事件做一个格式化。格式化字符串为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/fileNamefileExt)保存数据到数据库 这里是数据的插入操作我们不做详细解释直接看代码 //插入数据库//获取orm对象o : orm.NewOrm()//获取要插入的对象var article models.Article//给对象赋值article.ArtiName articleNamearticle.Acontent content//这一步需要注意我们存储的图片是图片地址没有 .article.Aimg /static/img/fileNamefileExt//插入o.Insert(article)返回视图 如果没有视图先返回一句话代码如下 this.Ctx.WriteString(添加成功)完整代码如下: //获取数据articleName : this.GetString(articleName)content : this.GetString(content)//数据校验if articleName || content {beego.Info(添加文章数据不完整请重新输入)this.TplName add.htmlreturn}//获取上传图片file,head,err : this.GetFile(uploadname)defer file.Close()if err ! nil{beego.Info(上传图片错误请重新添加)this.TplName add.htmlreturn}//文件格式判断fileExt : path.Ext(head.Filename)if fileExt ! .jpg fileExt ! .png fileExt ! .jpeg{beego.Info(上传图片格式不正确请重新添加)this.TplName add.htmlreturn}//文件大小判断if head.Size 5000000{beego.Info(上传图片太大请重新添加)this.TplName add.htmlreturn}//避免文件重名fileName : time.Now().Format(2006-01-02-15-04-05)this.SaveToFile(uploadname,./static/img/fileNamefileExt)//插入数据库//获取orm对象o : orm.NewOrm()//获取要插入的对象var article models.Article//给对象赋值article.ArtiName articleNamearticle.Acontent content//这一步需要注意我们存储的图片是图片地址没有.article.Aimg /static/img/fileNamefileExt//插入o.Insert(article)//返回视图this.Ctx.WriteString(添加成功)后台完整代码—添加文章内容 package controllersimport (github.com/beego/beego/v2/client/ormgithub.com/beego/beego/v2/core/logsbeego github.com/beego/beego/v2/server/webpathprojectName/modelstime )type ArticleController struct {beego.Controller }// 展示文章列表页 func (this *ArticleController) ShowArticleList() {this.TplName index.html }// 展示添加文章页面 func (this *ArticleController) ShowAddArticle() {this.TplName add.html }// 获取添加文章数据 func (this *ArticleController) HandleAddArticle() {//获取数据 这里获取数据是前端name值articleName : this.GetString(articleName)content : this.GetString(content)select1 : this.GetString(select)//校验数据if articleName || content {this.Data[errmsg] 添加数据不完整this.TplName add.html}logs.Info(chengpeng:, articleName, content, select1)//处理文件上传 文件流文件相关信息错误file, head, err : this.GetFile(uploadname)defer file.Close()if err ! nil {this.Data[errmsg] 文件上传失败this.TplName add.htmlreturn}//1 文件大小if head.Size 50000000 {this.Data[errmsg] 文件太大请重新上传this.TplName add.htmlreturn}//文件格式//a.jpg 获取后缀名ext : path.Ext(head.Filename)if ext ! .jpg ext ! .png ext ! .bmp ext ! .jpeg {this.Data[errmsg] 文件格式错误请重新上传this.TplName add.htmlreturn}//防止重名fileName : time.Now().Format(2006-01-02-15-04-05) ext//存储/**第一个参数是前端标签的name属性值第二个参数是文件在服务器端存储的位置。**注意:这个位置字符串在前面需要加一个*/err this.SaveToFile(uploadname, ./static/img/fileName)if err ! nil {logs.Info(保存前端出过来的文件)return}//数据处理//插入操作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/webprojectName/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后台代码 修改路由文件之后我们实现ShowArticleList函数。 5.2.1获取所有文章 获取orm对象 o : orm.NewOrm()定义一个对象数组(切片)用来存储获取的所有对象 var articles []models.Article指定要查询的数据库表用QueryTable函数参数是表名返回值是queryseterORM 以 QuerySeter 来组织查询每个返回 QuerySeter 的方法都会获得一个新的 QuerySeter 对象。 qs : o.QueryTable(Article)获取所有数据用all方法参数是对象数组地址 qs.All(articles)获取数据之后把数据传递给视图并且指定视图文件 this.Data[articles] articles this.TplName index.html在浏览器里面输入地址之后能获取下面页面表示代码没有问题 ORM高级查询**重点** 我们在后面项目开发中对数据库的查询一般都是指定数据库表用高级查询的方法进行查询。ORM支持如下几种高级查询。 函数名作用用法Limit()获取部分数据有两个参数第一个参数是指定获取几条数据第二个参数指定从哪里获取qs.Limit(size,start)。返回值还是qsOrderBy()根据指定的字段排序只有一个参数参数作用是指定按照哪个字段排序返回值是qsDistinct()去重没有参数返回值是qsCount()查询符合条件的数据条目数没有参数返回值是查询到的条目数和错误信息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}}trtd{{.ArtiName}}/tdtda href#查看详情/a/tdtd {{.Atime.Format 2006-01-02-15-04-05}}/tdtd{{.Acount}}/tdtda href# classdels删除/a/tdtda href#编辑/a/tdtd财经新闻/td/tr {{end}}实现之后我们就可以把添加文章最后的跳转改成显示文章列表页超链接和文章类型我们在接下来的页面实现。 5.4数据的分页显示(难点) 观察我们的列表页可以发现我们文章里表下面是分页展示接着我们来实现这个分页。分页功能在我们平常浏览网页的时候也比较常见这里我们先实现简单的分页功能等到我们项目实战的时候会给大家封装一个分页函数实现一个高级点的分页。首页显示如下 分页的好处如果没有分页我们访问完数据就要全部在页面显示有分页之后我们可以显示部分数据好处有一下两点。 方便浏览分页浏览可以更方便我们平常访问网页。提高访问网站速度。如果一次性把数据全部从数据库中取出来效率没有一次取出部分数据块。 了解了上面的内容之后我们开始写代码实现分页的功能一般开发中遇见这种大的功能模块我们都是划分为几个小块一点一点来实现。我们从简单到复杂来实现相应功能首先我们先获取总页数和总记录数。 5.4.1获取总记录数和总页数 首页显示代码是ShowArticleList函数所以我们分页的业务代码也在这个函数里面。 获取总记录数orm是用count函数来获取数据的记录数没有参数返回值为记录数和错误信息代码如下: counterr : 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?frindexfp0ieutf-8red_tagm2329796506我们重点关注?后面的内容他们是成对出现的每对之间用连接这种是URL传值的一种。我们在后台通过GetString函数可以获取到相应的值。 设置首页的超链接 我们可以通过URL传值的方式把页码传递过来。这里我们设置首页的标签超链接为/ShowArticleList?pageIndex1 获取首页数据 我们先通过GetString()获取到页码然后通过页码获取相应的数据。这里给大家介绍数据库获取部分数据的函数**Limit() ** Limit()-----这个有bug如果写在下面就会报错–指定之后要查询的数据库表以后必须先查询 **作用**获取数据库中部分数据 **参数:**第一个参数是获取多少数据第二个参数是从哪里开始取数据 返回值是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 worldreturn }添加映射 添加映射是把后台的函数名和视图中调用的函数名关联起来两个名字可以不一样。用的方法是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)在视图中调用 我们这里用第二种调用视图函数的方法 lia href/ShowArticleList?pageIndex{{pre .pageIndex}}上一页 /a /lili 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 }到这里我们的分页功能就完全实现了 完整后台代码 package controllersimport (github.com/beego/beego/v2/client/ormgithub.com/beego/beego/v2/core/logsbeego github.com/beego/beego/v2/server/webmathpathprojectName/modelstime )type ArticleController struct {beego.Controller }// 展示文章列表页 func (this *ArticleController) ShowArticleList() {//获取数据//高级查询pageSize : 2//获取页码pageIndex, err : this.GetInt(pageIndex)if err ! nil {pageIndex 1}//起始位置计算start : (pageIndex - 1) * pageSize//指定表newOrm : orm.NewOrm()//指定要查询的数据库表queryset : newOrm.QueryTable(Article) //QuerySeter//查询所有的 返回值查询多少数据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(查询数据错误, err1)return}//查询总记录数————总的记录数count, err : queryset.Count()if err ! nil {logs.Info(查询总记录数错误, err)return}//获取总页数pageCount : math.Ceil(float64(count) / float64(pageSize))//获取数据//logs.Info(chengpengaini, pageIndex)//传递数据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 }// 获取添加文章数据 func (this *ArticleController) HandleAddArticle() {//获取数据articleName : this.GetString(articleName)content : this.GetString(content)select1 : this.GetString(select)//校验数据if articleName || content {this.Data[errmsg] 添加数据不完整this.TplName add.html}logs.Info(chengpeng:, articleName, content, select1)//处理文件上传 文件流文件相关信息错误file, head, err : this.GetFile(uploadname)defer file.Close()if err ! nil {this.Data[errmsg] 文件上传失败this.TplName add.htmlreturn}if head.Filename {this.Data[errmsg] 请上传文件this.TplName add.htmlreturn}//1 文件大小if head.Size 50000000 {this.Data[errmsg] 文件太大请重新上传this.TplName add.htmlreturn}//文件格式//a.jpg 获取后缀名ext : path.Ext(head.Filename)if ext ! .jpg ext ! .png ext ! .bmp ext ! .jpeg {this.Data[errmsg] 文件格式错误请重新上传this.TplName add.htmlreturn}//防止重名fileName : time.Now().Format(2006-01-02-15-04-05) ext//存储/**第一个参数是前端标签的name属性值第二个参数是文件在服务器端存储的位置。**注意:这个位置字符串在前面需要加一个*/err this.SaveToFile(uploadname, ./static/img/fileName)if err ! nil {logs.Info(保存前端出过来的文件)return}//数据处理//插入操作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//项目运行期间操作数据库的代码_ projectName/models_ projectName/routers )func main() {beego.AddFuncMap(prepage, ShowPrePage)beego.AddFuncMap(nextpage, ShowNextPage)beego.Run() }// 后台定义函数----视图函数 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 langen headmeta charsetUTF-8title添加文章内容/titlelink relstylesheet typetext/css href/static/css/reset.csslink relstylesheet typetext/css href/static/css/main.css /head bodydiv classheadera href# classlogo flimg src/static/img/logo.png altlogo/aa href# classlogout fr退 出/a/divdiv classside_bardiv classuser_infoimg src/static/img/person.png alt张大山p欢迎你 em李雷/em/p/divdiv classmenu_condiv classfirst_menu activea hrefjavascript:; classicon02文章管理/a/divul classsub_menu showlia href# classicon031文章列表/a/lilia href# classicon032添加文章/a/lilia href# classicon034添加分类/a/li/ul/div/divdiv classmain_body idmain_bodydiv classbreadcrub当前位置文章管理添加文章/divdiv classpannelform methodpost action/addArticle enctypemultipart/form-datah3 classreview_title添加文章/h3div classform_grouplabel文章标题/labelinput typetext classinput_txt2 namearticleName /divdiv classform_grouplabel文章类型/labelselect classsel_opt nameselectoption体育新闻/optionoption财经新闻/optionoption科技新闻/option/select/divdiv classform_grouplabel文章内容/labeltextarea classinput_multxt namecontent/textarea/divdiv classform_grouplabel上传图片/labelinput typefile classinput_file nameuploadname/divdiv classform_group indent_group line_topinput typesubmit value添 加 classconfirmspan{{.errmsg}}/span/div/form/div /div/body /htmlindex.html !DOCTYPE html html langen headmeta charsetUTF-8title后台管理页面/titlelink relstylesheet typetext/css href/static/css/reset.csslink relstylesheet typetext/css href/static/css/main.cssscript typetext/javascript src/static/js/jquery-1.12.4.min.js/script/head bodydiv classheadera href# classlogo flimg src/static/img/logo.png altlogo/aa href# classlogout fr退 出/a/divdiv classside_bardiv classuser_infoimg src/static/img/person.png alt张大山p欢迎你 em李雷/em/p/divdiv classmenu_condiv classfirst_menu activea hrefjavascript:; classicon02文章管理/a/divul classsub_menu showlia href# classicon031文章列表/a/lilia href/addArticle classicon032添加文章/a/lilia href# classicon034添加分类/a/li/ul/div/divdiv classmain_body idmain_bodydiv classbreadcrub当前位置文章管理文章列表/divdiv classpannelspan classsel_label请选择文章分类/spanselect nameselect idselect classsel_optoption selectedtrue财经新闻/optionoption体育新闻/optionoption科技新闻/option/selecttable classcommon_tabletrth width43%文章标题/thth width10%文章内容/thth width16%添加时间/thth width7%阅读量/thth width7%删除/thth width7%编辑/thth width10%文章类型/th/tr{{range $index,$val : .articles}}trtd{{$val.ArtiName}}/tdtda href#查看详情/a/tdtd {{$val.Atime.Format 2006-01-02-15:04:05}}/tdtd{{$val.Acount}}/tdtda href# classdels删除/a/tdtda href#编辑/a/tdtd财经新闻/td/tr{{end}}/tableul classpagenationlia href/showArticleList?pageIndex1首页/a/lilia href/showArticleList?pageIndex{{.pageIndex | prepage }}上一页 /a /lilia href/showArticleList?pageIndex{{nextpage .pageIndex .pageCount}}下一页/a/lilia href/showArticleList?pageIndex{{.pageCount}}末页/a/lili共{{.count}}条记录/共{{.pageCount}}页/当前{{.pageIndex}}页/li/ul/div/div /body /html6.查看文章详情 业务流程图如下 首先我们还是需要设计一下查看详情的请求路径。分析可知我们查看文章详情必须指定要查看哪一篇文章所以我们在点击查看详情的时候需要把能够标识具体哪一篇文章的数据传递给后台这里我们通过URL传值的方式传递文章ID给后台设计路由为/ShowArticleDetail?idarticle.Id 6.1文章详情页面显示 前端处理 修改查看详情的超链接代码如下 tda hrefShowArticleDetail?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.Articlearticle.Id ido.Read(article)获取数据之后指定视图并给视图传递数据 //传递数据给视图,并指定视图this.Data[article] articlethis.TplName content.html完整代码如下 //获取文章idid,err : this.GetInt(id) //数据校验if err ! nil{beego.Info(请求路径错误)this.Redirect(/ShowArticleList,302)return} //查询数据o : orm.NewOrm()var article models.Articlearticle.Id ido.Read(article)//传递数据给视图,并指定视图this.Data[article] articlethis.TplName content.html访问浏览器查看页面如下 这时候页面显示的是假数据我们修改视图文件让页面显示的数据为我们添加的文章数据 视图文件修改,还没有添加的数据不做修改。 div classpannelh3 classreview_title文章详情/h3div classform_grouplabel文章标题/labelp classdetailb{{.article.ArtiName}}/b/p/divdiv classform_grouplabel文章类型/labelp classdetail体育新闻/p/divdiv classform_grouplabel文章内容/labelp classdetailimg src{{.article.Aimg}}{{.article.Acontent}}/p/divdiv classform_grouplabel阅读次数/labelp classdetail{{.article.Acount}}/p/divdiv classform_grouplabel最近浏览/labelp classdetail张三 | 李四 |/p/divdiv classform_grouplabel创建时间/labelp classdetail{{.article.Atime.Format 2006-01-02-15-04-05}}/pspan{{.errmsg}}/span/div /div保存之后再次刷新页面显示如下 我们查看详情页面的显示这部分就实现了。 6.2阅读次数增加 每次查看详情其实就是阅读次数的增加我们需要在查看详情函数里面给阅读次数加一代码如下: //给查询出来的文章阅读次数加一article.Acount 1o.Update(article)7.编辑文章内容 7.1编辑页面显示 业务流程图如下: 编辑页面显示和文章详情页面处理流程基本一样,也同样需要传递文章ID先需要确定请求路径这里我们设置请求路径为UpdateArticle?idarticle.id修改路由文件代码如下: beego.Router(/UpdateArticle,controllers.ArticleController{},get:ShowUpdateArticle)然后在后台查询数据传递给视图逻辑重复就不详细分析了代码如下 //展示编辑文章界面 func(this*ArticleController)ShowUpdateArticle(){//获取文章idid,err : this.GetInt(id)//数据校验if err ! nil{beego.Info(请求路径错误)this.Redirect(/ShowArticleList,302)return}//查询数据o : orm.NewOrm()var article models.Articlearticle.Id ido.Read(article)//传递数据给视图,并指定视图this.Data[article] articlethis.TplName update.html }前端数据展示代码如下 !DOCTYPE html html langen headmeta charsetUTF-8title更新文章内容/titlelink relstylesheet typetext/css href/static/css/reset.csslink relstylesheet typetext/css href/static/css/main.css /head bodydiv classheadera href# classlogo flimg src/static/img/logo.png altlogo/aa href# classlogout fr退 出/a/divdiv classside_bardiv classuser_infoimg src/static/img/person.png alt张大山p欢迎你 em李雷/em/p/divdiv classmenu_condiv classfirst_menu activea hrefjavascript:; classicon02文章管理/a/divul classsub_menu showlia href# classicon031文章列表/a/lilia href# classicon032添加文章/a/lilia href# classicon034添加分类/a/li/ul/div/divdiv classmain_body idmain_bodydiv classbreadcrub当前位置文章管理编辑文章/divdiv classpannelform name logon h3 classreview_title编辑文章/h3div classform_grouplabel文章标题/labelinput typetext classinput_txt2 name articleName value{{.article.ArtiName}}/divdiv classform_grouplabel文章内容/labeltextarea classinput_multxt namecontent{{.article.Acontent}}/textarea/divdiv classform_grouplabel上传图片/labelimg src{{.article.Aimg}}input typefile nameuploadname classinput_file/divdiv classform_group indent_group line_topinput typesubmit value添 加 classconfirmspan{{.errmsg}}/span/div/form/div/div /body /html 在浏览器输入http://192.168.110.73:8080/UpdateArticle?id1显示如下 7.2编辑文章数据 这一步其实是对查询到的文章进行更新操作我们还用获取页面时的请求路径UpdateArticle?idarticle.id,但是请求改为post请求form标签修改如下 form name logon methodpost action/UpdateArticle?id{{.article.Id}} enctypemultipart/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/fileNamefileExt)return /static/img/fileNamefileExt } //处理更新数据 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?idstrconv.Itoa(id),302)return}//更新数据o : orm.NewOrm()var article models.Articlearticle.Id idif err : o.Read(article);err ! nil{beego.Info(传递的文章id错误)this.Redirect(/UpdateArticle?idstrconv.Itoa(id),302)return}article.ArtiName articleNamearticle.Acontent contentarticle.Aimg imgo.Update(article)//返回视图this.Redirect(/ShowArticleList,302) }8.删除文章 8.1删除功能实现 业务流程图如下: 删除功能相比较前面的功能算是比较简单的只需要传递过来文章id值然后删除文章即可。 首先我们还是要指定删除文章的请求路径DeleteArticle?idarticle.Id 然后修改路由文件为删除请求指定控制器指定函数。 beego.Router(/DeleteArticle,controllers.ArticleController{},get:DeleteArticle)然后在后台实现DeleteArticle函数,代码如下 //删除文章 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) }这时候你发现功能实现了但是存在误删的可能整个页面显的特别不友好我们给页面加个js提示防止误删。 8.2删除js提示 业务分析:当点击删除超链接的时候弹出对话框如果确认就发送请求如果取消就不发送请求代码如下 script typetext/javascript$(.dels).click(function () {if(!confirm(是否确认删除)){return false}}) /script1.类型相关内容 在实现类型相关业务之前我们先创建类型表。这里我们添加上一对多多对多的关系。 一个类型下面有很多篇文章但是一篇文章只属于一个类型所以文章与类型属于一对多。 同时我们分析一个用户可以阅读多篇文章一篇文章也可以被多个用户阅读所以文章和用户之间属于多对多关系。 由此我们开始建表建表代码如下我们根据代码分析一对多多对多如何设置 type User struct {Id intName string orm:uniquePasswd string orm:size(20)Articles []*Article orm:rel(m2m) //设置多对多关系 } //文章结构体 type Article struct {Id int orm:pk;autoArtiName string orm:size(20)Atime time.Time orm:auto_nowAcount int orm:default(0);nullAcontent string orm:size(500)Aimg string orm:size(100)ArticleType*ArticleType orm:rel(fk) //设置一对多关系Users []*User orm:reverse(many) //设置多对多的反向关系 } //类型表 type ArticleType struct {Id intTname string orm:size(20)Articles []*Article orm:reverse(many) //设置一对多的反向关系 }func init(){//1.连接数据库orm.RegisterDataBase(default,mysql,root:123456tcp(127.0.0.1:3306)/test?charsetutf8)//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:“reverse(one)” **一对多 ** 关系设置一对多中两表之间的关系不可互换,以文章表和类型表为例,当创建表的时候 在 文章表对应的文章结构体中添加类型表的对象指针并且设置一对多关系(orm:“rel(fk)”), 在 类型张表对应的结构体中添加文章表的对象指针数组并且设置一对多的反向关系orm:“reverse(many)” 生成表的时候数据库会自动在 文章表中添加类型表的表的Id作为文章表的外键。如图 一对多插入操作只需要在文章表插入类型对象即可。代码如下 o : orm.NewOrm() article : models.Article{} artiType : models.ArticleType{Id:id} o.Read(artiType) article.ArticleType artiType o.Insert(article)一对多查询 ORM做多表查询的时候默认是惰性查询即不明确指出来要做多表查询即便是两个表之间存在关系ORM也不会给两个表做关联。指定多表查询的函数是RelatedSel()。参数是要关联的表名可以有多个。代码如下 count,err o.QueryTable(Article).RelatedSel(ArticleType).Count()如果关联表的那个字段没有值那么数据查不到 多对多 关系设置多对多中两表之间的关系是平等的所以他们的属性设置可以呼唤,以文章表和用户表为例,当创建表的时候 在 文章表对应的文章结构体中添加用户表的对象指针数组并且设置多对多关系(orm:“rel(m2m)”), 在用户表对应的结构体中添加文章表的对象指针数组并且设置多对多的反向关系orm:“reverse(many)” 生成表的时候数据库会生成一个用户和文章之间的关系表有三个字段Id,用户表Id文章表ID。如下图 多对多插入操作 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页面显示如下 由页面可知我们添加文章类型界面分两块一块是上面以表格的形式显示所有类型一块是下面增加分类。我们先来处理增加分类。 1.1.2添加类型数据处理 添加类型业务比较简单首先是修改我们的视图页面内容给form标签请求方式和请求路径代码如下: form methodpost 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.ArticleTypearticleType.Tname typeNameif _,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.ArticleTypeo.QueryTable(ArticleType).All(articleTypes)//传递数据给视图并指定视图this.Data[articleTypes] articleTypesthis.TplName addType.html }视图代码 在视图页面中我们循环控制器传递过来的数组拿到我们需要的数据 {{range .articleTypes}}trtd{{.Id}}/tdtd{{.Tname}}/tdtda hrefjavascript:; classedit删除/a/td/tr {{end}}这时候我们在浏览器输入地址http://192.168.110.75:8080/addArticleType,得到如下页面 添加一个类型测试然后发现页面还是没有类型显示这个说明我们代码处理出问题了哪里出问题了呢还记得前面给大家留的思考题吗我们添加完文章类型之后是直接渲染加载了视图这时候并没有给视图传递数据所以也就没有类型显示。这样的结果和我们的业务 不符合所以我们需要把添加完类型之后跳转页面的方式改为重定向然后再看结果发现类型显示正常。 1.2首页根据下拉框选项不同获取不同类型数据 现在有类型数据了我们添加文章的时候也需要添加上类型了。 1.2.1添加带类型的文章 在展示页面的时候需要把类型数据绑定添加类型的下拉框 后台获取数据在展示添加文章界面那个函数里面写相关代码 //展示添加文章界面 func (this*ArticleController)ShowAddArticle(){//查询数据o : orm.NewOrm()var articleTypes []models.ArticleTypeo.QueryTable(ArticleType).All(articleTypes)//传递数据给视图并指定视图this.Data[articleTypes] articleTypesthis.TplName add.html }视图展示数据 循环获取数据在下拉框中显示类型名称 select classsel_opt nameselect{{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列表页展示文章时展示类型信息。 查询所有问章关联文章类型表查询的时候加上RelatedSel(“ArticleType”),代码如下 qs.Limit(pageSize,start).RelatedSel(ArticleType).All(articles)显示的时候显示出来 {{range .articles}}trtd{{.ArtiName}}/tdtda hrefShowArticleDetail?id{{.Id}}查看详情/a/tdtd {{.Atime.Format 2006-01-02-15-04-05}}/tdtd{{.Acount}}/tdtda href/DeleteArticle?id{{.Id}} classdels删除/a/tdtda hrefUpdateArticle?id{{.Id}}编辑/a/tdtd{{.ArticleType.Tname}}/td/tr {{end}}这时候你发现以前添加的文章都没有显示还记得我们前面介绍多表操作的时候介绍的吗加上RelatedSel之后如果相应的字段没有数据将查询不出来。 1.2.3根据下拉框选项不同获取不同类型数据难点 查询类型数据并把数据绑定到下拉框 这个业务代码和添加文章的业务代码一样我们就不做详细分析直接看代码 //查询数据var articleTypes []models.ArticleTypeo.QueryTable(ArticleType).All(articleTypes)this.Data[articleTypes] articleTypes视图代码 select nameselect idselect classsel_opt{{range .articleTypes}}option selectedtrue{{.Tname}}/option{{end}} /select根据下拉框选中类型获取相同类型的文章 把选中的类型数据传递给后台 我们以前传递数据是用form表单这里我们还是用form表单把下拉框包起来然后把选中的数据传递给后台。代码如下 form methodget action/ShowArticleListselect nameselect idselect classsel_opt{{range .articleTypes}}option selectedtrue{{.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.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)//查询数据以及分页显示o : orm.NewOrm()qs : o.QueryTable(Article)var count int64//数据校验sif 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)}//查询数据库部分数据//获取类型数据//查询数据var articleTypes []models.ArticleTypeo.QueryTable(ArticleType).All(articleTypes)this.Data[articleTypes] articleTypesthis.Data[count] countthis.Data[pageCount] int(pageCount)this.Data[pageIndex] pageIndex//传递数据并指定视图this.Data[articles] articlesthis.TplName index.html }这时候你再看页面会发现一个问题有一个选项一直都不能够选中。为什么呢 是因为我们每一次改变下拉框的选项都会让js发出get请求给后台后台就会重新查询所有的类型表绑定下拉框所以每次显示的都是一个数据这样的话我们选中显示的那条数据就无法触发js发送请求因为js认为下拉框显示内容并没有变化。这时候下拉框显示也有问题那怎么解决这个问题呢 1.2.4解决下拉框选项显示的问题 ​ 通过前面的分析我们知道每次下拉框都是重新从数据库中获取类型数据进行绑定这里面我们就需要对选中的类型加一个判断当从数据库中取出的数据是选中的类型时就给下拉框选项属性selected设置为true。首先后台要传递当前选中的类型名称给视图代码如下 //传递当下拉框选择的类型名给视图 this.Data[typeName] typeName前端代码处理 视图中我们接收控制器传递过来的当前选中类型然后与数据库中的类型名进行比较如果相同就设置选中不同就不设置代码如下 select nameselect idselect classsel_opt{{range .articleTypes}}{{if compare .Tname $.typeName}}option selectedtrue{{.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功能需要先在配置文件中设置Sessionontrue 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值我们通过表格来分析他们的不同 不同点CookieSession数据存储位置客户端服务器数据安全性相比较而言低高生命周期随着设置时间的结束生命周期结束当浏览器关闭的时候生命周期结束适用场景对安全性要求不高的需要存储时间较长的数据安全性要求搞不需要长期存储的数据 简单了解了这两个知识点之后我们来看一下如何实现我们项目剩余的四个功能。 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] userNamethis.Data[checked] checked }else{this.Data[userName] this.Data[checked] }视图中接收数据 form classlogin_form name login action/login methodposth1 classlogin_title用户登录/h1input typetext classinput_txt name userName value{{.userName}}input typepassword name passwd classinput_txtdiv classrememberinput typecheckbox nameremember {{.checked}} label记住用户名/label/divinput typesubmit value登 录 classinput_sub /form注意当checkbox添加一个checked属性时checkbox就为选中状态 2.2登陆判断 因为我们操作的都是后台管理界面所以我们需要做登陆判断。我们这里面用Session来实现这个功能。 在使用Session之前记得要在配置文件中设置sessionontrue 当登陆成功之后就设置Session代码如下 //设置session this.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 classlogout fr退 出/a接着我们在路由中指定请求对应的控制器和方法 beego.Router(/logout,controllers.ArticleController{},get:Logout)然后我们实现一个Logout函数业务逻辑很简单我们直接看代码 //退出登录 func(this*ArticleController)Logout(){//删除sessionthis.DelSession(userName)//跳转this.Redirect(/login,302) }2.4最近浏览 最近浏览也就是在我们浏览文章的时候给文章添加上用户信息然后在再查询这些信息在页面中显示。 添加浏览信息 我们这里是给文章表添加浏览的用户信息。代码如下 //获取ORM对象 o : orm.NewOrm() //获取插入数据的对象 var article models.Article article.Id id o.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 classdetail{{range .article.Users}}{{.Name}} | {{end}}/p这时候我们多点几次查看详情会发现个问题我们添加关系的时候是浏览一次就添加一次那么我们显示的时候就会重复显示相同用户的用户名效果如下 但是我们一般浏览网页的时候一个用户浏览过了只显示一次该用户信息即可所以这里面我们需要去重还记得我们前面介绍的高级查询去重的方法吗Distinct()去重但是这个函数必须要是queryseter对象才能操作所以我们第一种多对多查询方法就不行了。这里我们用第二种多对多查询。代码如下 var users []models.User o.QueryTable(User).Filter(Articles__Article__Id,article.Id).Distinct().All(users)注意我们这里插入的是想article中插入user但是查询的是从user中去获取。 3.项目优化 3.1路由过滤器 我们在项目实现的时候只给文章列表页和详情页添加了登陆判断我们思考一下我们这个案例其实是整个的后台管理所以每个页面都需要添加登陆判断那我们就需要每个地方都要添加登陆判断重复代码很多。这里给大家介绍一个新的技术路由过滤器在路由层面添加一个过滤实现登陆判断。那我们来看一下什么是路由过滤器。 作用可以根据指定的匹配规则在特定的项目运行阶段去执行自定义函数函数一般放在beego.router()之前 。 那我们看一下路由过滤器函数的格式 beego.InsertFilter(/article/*, position int, filter FilterFunc)第一个参数是路由匹配规则支持正则 第二个参数是指定项目运行阶段在beego项目运行过程中框架帮我们分了五个阶段分别是 a) BeforeStatic 静态地址之前 b) BeforeRouter 寻找路由之前 c) BeforeExec 找到路由之后开始执行相应的 Controller 之前 d) AfterExec 执行完 Controller 逻辑之后执行的过滤器 e) FinishRouter 执行完逻辑之后执行的过滤器 具体对应是如下这种图的时间点 第三个参数就是指定过滤器函数。 路由过滤器一般放在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视图布局 实现了过滤器函数之后我们再来看我们整个项目页面显示如下 你会发现有些内容在每个页面中都有显示那我们能不能避免这些重复操作呢这里给大家介绍一个新的知识点视图布局 **作用:**通过设置模板页面其他页面可以直接调用模板避免再次处理重复代码。 视图布局本质上就是两个html界面的拼接比如我们现在有一个包含重复部分的html界面layout.html还有一个只包含添加文章业务的界面我们可以根据如下去实现两个页面的拼接 。 操作如下 控制器代码如下 this.Layout layout.html this.TplName add.htmllayout.html中的代码 !DOCTYPE html html langen headmeta charsetUTF-8title后台管理页面/titlelink relstylesheet typetext/css href/static/css/reset.csslink relstylesheet typetext/css href/static/css/main.cssscript typetext/javascript src/static/js/jquery-1.12.4.min.js/script/head bodydiv classheadera href# classlogo flimg src/static/img/logo.png altlogo/aa href/logout classlogout fr退 出/a /divdiv classside_bardiv classuser_infoimg src/static/img/person.png alt张大山p欢迎你 em李雷/em/p/divdiv classmenu_condiv classfirst_menu activea hrefjavascript:; classicon02文章管理/a/divul classsub_menu showlia href# classicon031文章列表/a/lilia href/addArticle classicon032添加文章/a/lilia href# classicon034添加分类/a/li/ul/div /div{{.LayoutContent}}/body /html注意这里面的 {{.LayoutContent}}这个标签的地方就是用来存放add.html的地方。 add.html中就可以删除掉相同的代码代码如下: div classmain_body idmain_bodydiv classbreadcrub当前位置文章管理添加文章/divdiv classpannelform methodpost action/addArticle enctypemultipart/form-datah3 classreview_title添加文章/h3div classform_grouplabel文章标题/labelinput typetext classinput_txt2 namearticleName /divdiv classform_grouplabel文章类型/labelselect classsel_opt nameselect{{range .articleTypes}}option{{.Tname}}/option{{end}}/select/divdiv classform_grouplabel文章内容/labeltextarea classinput_multxt namecontent/textarea/divdiv classform_grouplabel上传图片/labelinput typefile classinput_file nameuploadname/divdiv classform_group indent_group line_topinput typesubmit value添 加 classconfirmspan{{.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 langen headmeta charsetUTF-8title后台管理页面/titlelink relstylesheet typetext/css href/static/css/reset.csslink relstylesheet typetext/css href/static/css/main.cssscript typetext/javascript src/static/js/jquery-1.12.4.min.js/script/head bodydiv classheadera href# classlogo flimg src/static/img/logo.png altlogo/aa href/logout classlogout fr退 出/a /divdiv classside_bardiv classuser_infoimg src/static/img/person.png alt张大山p欢迎你 em李雷/em/p/divdiv classmenu_condiv classfirst_menu activea hrefjavascript:; classicon02文章管理/a/divul classsub_menu showlia href# classicon031文章列表/a/lilia href/addArticle classicon032添加文章/a/lilia href# classicon034添加分类/a/li/ul/div /div{{.LayoutContent}}/body /html {{.Scripts}}3.3补充 我们回顾一下看看我们的项目还有哪点没有实现呢类型的删除是不是还没有实现可能有的学生会说老师这个删除和文章的删除一样直接删除不久行了嘛这里老师要特别提醒**类型是与多表操作有关的删除效果和单表的文章不一样 ** 那我们来看一下类型的删除 同样还是四步骤**请求-路由-控制器-视图 ** 请求 删除类型是在添加类型页面中实现的在这个页面中有一个删除的标签如下图所示 源码路径https://gitee.com/cheng-penga/chengpeng-beego-go
http://www.ho-use.cn/article/10821348.html

相关文章:

  • nas搭建网站重庆机械加工网
  • 旅游景点企业网站排名东莞是哪个省
  • 苏州知名网站建设开发网站建设如何设定关键字
  • 生物科技网站模板wordpress评论积分
  • 深圳住房和建设局网站公开招标asp php jsp网站开发
  • 网站建设cz35wordpress 禁止页面评论
  • 写wordpressseo定义
  • 网站管理方案个人网站怎么备案
  • 辽宁省建设工程注册中心网站没备案的网站怎么做淘客
  • 网站开发公司飞沐做网站赚钱吗
  • windows优化大师是什么合肥网络公司seo
  • 株洲网站建设哪家好wordpress canvas 粒子跟随特效
  • 固镇网站建设如何组织公司做网站
  • 网银网站模板网站建设怎么制作模板
  • 广州模板网站建设费用备案变更网站信息
  • wordpress建一个网站吗福永医院网站建设
  • 上海免费建网站深圳画册设计工作室
  • 网站建设咨询有客诚信网站建网站半年了 没有流量
  • 网站治做啊app房屋租赁系统网站开发
  • 什么叫做响应式网站网络营销网站设计
  • 30天网站建设实录素材wordpress标签是什么
  • 网站设计培训学校找哪家头像制作器在线制作
  • 毕业设计和论文网站做网站公司项目的流程
  • 昆明优化网站公司网站技术方案
  • 上海外贸网站制作花瓣网免费素材图库官网
  • 做一份seo网站诊断美工网站做兼职
  • windows 2003做网站浏览器正能量网站免费
  • 深圳市城乡建设部网站首页网站建设品牌策划方案
  • 地区网站建设服务周到官方网站如何建立
  • 门户网站建设管理工作的意见公司官网怎么做的