如何从视图中*sql.rows类型的变量中获取值?

qyuhtwio  于 2021-06-20  发布在  Mysql
关注(0)|答案(1)|浏览(435)

posts 表中,有如下属性 title , content . 我可以得到 *sql.Rows 把它们传给观景台

posts, err := db.Query("SELECT id, title FROM posts WHERE id = 1")

以及

err = tpl.ExecuteTemplate(w, "index.gohtml", posts)

但我无法显示 title 视图中的值。这是我的密码。
索引.go

package main

import (
  "net/http"
  "fmt"
  "log"
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
)

func index(w http.ResponseWriter, r *http.Request) {
  db, err := sql.Open("mysql", "root:****@/database")
  if err != nil {
    panic(err.Error())
  }
  defer db.Close()

  posts, err := db.Query("SELECT id, title FROM posts WHERE id = 1")
  var id int
  var title string
  for posts.Next() {
    err = posts.Scan(&id, &title)
    if err != nil {
      panic(err.Error())
    }
  }

  fmt.Println(posts)

  defer posts.Close()

  err = tpl.ExecuteTemplate(w, "index.gohtml", posts)
  if err != nil {
    log.Println(err)
  }
}

索引.gohtml

<h1>Awesome Posts</h1>

  {{$posts := .}}
  <p>{{$posts}}</p>
vnzz0bqm

vnzz0bqm1#

您的代码中有一些错误,我认为您误解了如何使用 sql 包裹。
正如flimzy在评论中所说,您应该传递一个适当的上下文结构,其中包含您的id和title值。
如果您检查文档中的sql.rows,您将看到如何从查询中提取每一行的数据……事实上,您已经知道如何通过使用 Next() 以及 Scan() 方法。但是这不应该由html模板中的代码来完成,它应该将结果存储在传递给模板的某个变量中。

快速回答

快速回答您的问题的方法是更改如何将值传递到模板中并修改模板。既然你宣布 id 以及 title 应将其传递给模板的变量:

err = tpl.ExecuteTemplate(w, "index.gohtml", map[string]interface{}{"ID": id,"Title": title})
if err != nil {
  log.Println(err)
}

<h1>Awesome Posts</h1>
<p>{{.ID}} - {{.Title}}</p>

有个模特儿

更好的解决方案是拥有一个包含post所有属性的结构,并使用它来 Scan 进入。

type Post struct{
  ID int
  Title string
}

...
var p Post
...
_ = rows.Scan(&p)

但是,存储查询结果的方式还有另一个问题。您正在使用 db.Query 返回一行-这是一个假设 WHERE ID=1 . 如果您只希望返回一个post,那么请使用queryrow方法:(注意,为了简单起见,您可以链接scan方法)

var p Post
// error handling removed for brevity
_ = db.QueryRow("SELECT id, title FROM posts WHERE id = 1").Scan(&p)
_ = tpl.ExecuteTemplate(w, "index.gohtml", p)

但是,如果您希望检索多个post(为了简单起见,您只是添加了where子句),那么您需要 Scan 变成一个 Post 结构,并附加到 Posts .

rows, _ := db.Query("SELECT id, title FROM posts")
defer rows.Close()
var posts []Post
for rows.Next() {
  var p Post
  _ = posts.Scan(&id, &p) // you should handle error here.
  posts = append(posts, p)
}
if err = tpl.ExecuteTemplate(w, "index.gohtml", posts); err!=nil{
  log.Println(err)
}

其他考虑因素

您不应该在http处理程序中创建到db的连接。一种方法是使用一个全局变量来保存连接。具有嵌入连接的结构可以工作和/或也可以将连接抽象到包中。
/分贝/分贝

package db

import (
    "database/sql"
    // MYSQL driver
    _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

// Open handles the opening of the DB
func Open(connstr string) (err error) {
    db, err = sql.Open("mysql", connstr)
        if err != nil {
            return err
        }
    return nil
}

// Close handles the closing of the DB
func Close() error {
    return db.Close()
}

/db/posts.go文件

package db

// Post model
type Post struct {
    ID    uint
    Title string
    Body  string
}

const (
    getPosts = `SELECT id, title, body FROM posts`
    getAPost = `SELECT id, title, body FROM posts WHERE id=?`
)

// GetPosts will return all posts from the DB
func GetPosts() ([]Post, error) {
    rows, err := db.Query(getPosts)
    if err != nil {
        return nil, err
    }
    var posts []Post
    for rows.Next() {
        var p Post
        if err := rows.Scan(&p.ID, &p.Title, &p.Body); err != nil {
            return nil, err
        }
        posts = append(posts, p)
    }

    return posts, nil
}

// GetPost will return single post identified by ID from the DB
func GetPost(id uint) (Post, error) {
    var p Post
    if err := db.QueryRow(getAPost, id).Scan(&p.ID, &p.Title, &p.Body); err != nil {
        return p, err
    }
    return p, nil
}

主菜单.go

import (
    "chilledoj/sopost/db" // this is the absolute path to the db folder 
    "html/template"
    "log"
    "net/http"
    "strconv"
    "flag"

    "github.com/gorilla/mux"
)

var dbconn string

func init() {
    flag.StringVar(&dbconn, "dbconn", "", "MYSQL DB Connection string")
    flag.Parse()
}

func main() {

    if dbconn == "" {
        log.Fatal("DB Connection string not set")
    }

    if err := db.Open(dbconn); err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    r := mux.NewRouter()    

    r.HandleFunc("/", indexHandler())
    r.HandleFunc("/posts", postsHandler())
    r.HandleFunc("/posts/{id}", postHandler())

    if err := http.ListenAndServe(":8080", r); err != nil {
        log.Panic(err)
    }
}

var indexHandler = func() http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if r.URL.Path != "/" {
            http.NotFound(w, r)
            return
        }
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`<h1>Welcome</h1><a href="/posts">Posts</a>`))
    }
}

var postsHandler = func() http.HandlerFunc {
    tmpl, err := template.New("posts").Parse(`<h1>Awesome Posts</h1>
  <ul>{{range .}}
  <li><a href="/posts/{{.ID}}">{{.Title}}</a></li>
  {{end}}</ul>
  <hr/>
  <a href="/">Home</a>`)
    if err != nil {
        log.Panic(err)
    }
    return func(w http.ResponseWriter, r *http.Request) {
        posts, err := db.GetPosts()
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(err.Error()))
            return
        }
        err = tmpl.Execute(w, posts)
        if err != nil {
            log.Printf("There was a template Error.\n%v\n", err)
        }
    }
}

var postHandler = func() http.HandlerFunc {
    tmpl, err := template.New("posts").Parse(`<h1>Awesome Posts</h1>
  <h2>{{.Title}}</h2>
  <p>{{.Body}}</p>
  <hr/>
  <a href="/">Home</a>
  <a href="/posts">Posts</a>`)
    if err != nil {
        log.Panic(err)
    }
    return func(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        id, err := strconv.ParseInt(vars["id"], 10, 32)
        if err != nil {
            http.NotFound(w, r)
            return
        }
        post, err := db.GetPost(uint(id))
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(err.Error()))
            return
        }
        tmpl.Execute(w, post)
    }
}

使用

go run main.go -dbconn [dbuser]:[dbpass]@/[dbname]?parseTime=true

另一种方法是使用依赖注入,让函数接收db连接,但返回 http.HandlerFunc . 例如

var indexHandler = function (db *sql.DB) http.HandlerFunc{
  return function(w http.ResponseWriter, r *http.Request){
    // now have access to db
  }
}

http.HandleFunc("/posts", indexHandler())

相关问题