mysql - 如何从视图中的 *sql.Rows 类型的变量中获取值?
问题描述
在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)
}
}
index.gohtml
<h1>Awesome Posts</h1>
{{$posts := .}}
<p>{{$posts}}</p>
解决方案
您的代码中有一些错误,我认为您误解了如何使用sql
包提取数据。
正如 Flimzy 在评论中所说,您应该传递一个适当的上下文结构,其中包含您的 ID 和 Title 值。
如果您查看sql.Rows 的文档,您将看到如何从查询中提取每一行的数据……事实上,您已经知道如何获取行和列值 - 通过使用Next()
andScan()
方法。但这不应该通过 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
。如果您希望只返回一篇文章,请使用 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)
但是,如果您希望检索多个帖子(并且为了简单起见,您只是添加了 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/db.go
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
}
main.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())
推荐阅读
- javascript - 通过使用 Map Filter 与对象数组进行比较来返回字段
- java - 执行 jar 文件时遇到问题
- php - 如何将图像路径存储在数据库中 - 使用 laravel
- scalatest - 为什么我不能测试一个类的实例是否已定义
- html - 在 Bootstrap 中,如何将所有元素保持在同一行?
- cakephp - 使用 composer 更新 cakephp3 导致缺少依赖项
- postgresql - 如何将整个数据库从 Postgres 迁移到 DynamoDB
- javascript - Ajax 从网格中删除行
- php - 如何在 sql 查询中设置 post 变量以获取两个日期之间的差异
- anylogic - 离散事件模拟中代理之间的空间