在deselect
表中,有诸如posts
,title
之类的属性。
我可以获得content
类型的数据并将其传递给视图,
*sql.Rows
和
posts, err := db.Query("SELECT id, title FROM posts WHERE id = 1")
但是我无法在视图中显示 err = tpl.ExecuteTemplate(w, "index.gohtml", posts)
值。这是我的代码。
index.go
title
index.gohtml
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)
}
}
答案 0 :(得分:2)
您的代码中存在一些错误,我认为您误解了如何使用sql
包提取数据。
正如Flimzy在评论中所说,您应该传递适当的上下文结构,该结构在某处包含ID和Title值。
如果您选中docs for sql.Rows,您将看到如何从查询中提取每一行的数据……实际上,您已经知道如何获取行和列的值-使用{ {1}}和Next()
方法。但这不应该通过HTML模板中的代码来完成,它应该将结果存储在传递给模板的某个变量中。
您的问题的快速答案是更改将值传递到模板中并修改模板的方式。看到声明了Scan()
和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>
。
Scan
但是,存储查询结果的方式还有另一个问题。您正在使用type Post struct{
ID int
Title string
}
...
var p Post
...
_ = rows.Scan(&p)
返回一行-这是一个假设,因为您有db.Query
。如果您只希望返回一篇帖子,请使用QueryRow方法:( N.B。您可以链接到Scan方法以简化操作)
WHERE ID=1
但是,如果您希望检索多个帖子(为了简单起见,您只是添加了where子句),那么您需要将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)
放入Scan
结构中,并追加到切片中的Post
。
Posts
您不应在HTTP处理程序中创建与数据库的连接。一种方法是拥有一个保存连接的全局变量。具有嵌入式连接的结构可以工作,并且/或者也可以将连接抽象为包。
/db/db.go
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)
}
/db/posts.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()
}
main.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
}
使用以下命令运行以上操作
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)
}
}
另一种方法是使用依赖项注入并使函数接受db连接,但返回go run main.go -dbconn [dbuser]:[dbpass]@/[dbname]?parseTime=true
。例如
http.HandlerFunc