去,调用t.Execute时有太多的参数。

huangapple go评论98阅读模式
英文:

Go, to many arguments in call t.Execute

问题

我正在尝试使用从数据库表中获取的值来渲染模板。
问题是,当我运行程序时,出现了错误。我不知道我做错了什么。

我开始创建一个结构体:

type App struct{
    Title string
    Author string
    Description string
}

我创建了一个函数来渲染模板:

func render(w http.ResponseWriter, tmpl string, data map[string]interface{}){
    tmpl = fmt.Sprintf("templates/%s", tmpl)
    t, err := template.ParseFiles(tmpl)
    if err != nil{
        log.Print("template parsing error: ", err)
    }
    err = t.Execute(w, data)
    if err != nil{
        log.Print("template executing error: ", err)
    }
}

然后,我从数据库中获取应用程序,并尝试将它们渲染到HTML中。

func myappsHandler(w http.ResponseWriter, r *http.Request){
    db, err := sql.Open("postgres"," user=postgres dbname=lesson4 host=localhost password=1234 sslmode=disable")
    if err != nil{
        log.Fatal(err)
    }
    defer db.Close()

    rows, err := db.Query(`SELECT title, author, description FROM apps
                         WHERE title ILIKE $1
                         OR author ILIKE $1
                         OR description ILIKE $1`)
    if err != nil{
        log.Fatal(err)
    }
    defer rows.Close()

    apps := []App{}
    for rows.Next(){
        b := App{}
        err := rows.Scan(&b.Title, &b.Author, &b.Description)

        if err != nil{
            log.Fatal(err)
        }
        apps = append(apps, b)
    }

    render(w, "myapps.html", map[string]interface{}{"apps" : apps})
}

我的主函数:

func main() {
    http.HandleFunc("/myapps", myappsHandler)
    http.ListenAndServe(":8080", nil)
}

这是myapps.html:

<html>
<body>
<table class="table">
  <tr>
    <th>标题</th>
    <th>作者</th>
    <th>描述</th>
  </tr>
{{ range .}}
  <tr>
    <td>{{ .Title }}</td>
    <td>{{ .Author }}</td>
    <td>{{ .Description }}</td>
    <td> <form action="/delete">
          <p class="navbar-form navbar-right"><button type="submit" class="btn btn-danger">删除</button> </p>
       </form></td>
  </tr>
{{ end }}
</table>
</body>
</html>
英文:

Im trying to render a template with values that I got from a table of a db.
The problem is that when I run the program I got errors. I dont know what Im doing wrong.

I started creating an estructure:

type App struct{
    Title string
    Author string
    Description string
}

I created a function to render the templates:

   func render(w http.ResponseWriter, tmpl string, data map[string]interface{}){
    tmpl = fmt.Sprintf(&quot;templates/%s&quot;, tmpl)
    t, err := template.ParseFiles(tmpl)
    if err != nil{
        log.Print(&quot;template parsing error: &quot;, err)
    }
    err = t.Execute(w, data)
    if err != nil{
        log.Print(&quot;template executing error: &quot;, err)
    }
}

Then, here I got the apps from the database and try to render them to the html.

func myappsHandler(w http.ResponseWriter, r *http.Request){
    db, err := sql.Open(&quot;postgres&quot;,&quot; user=postgres dbname=lesson4 host=localhost password=1234 sslmode=disable&quot;)
    if err != nil{
        log.Fatal(err)
    }
        rows, err := db.Query(`SELECT title, author, description FROM apps
                                 WHERE title ILIKE $1
                                 OR author ILIKE $1
                                 OR description ILIKE $1`)
            defer rows.Close()

            apps := []App{}
            for rows.Next(){
                b := App{}
                err := rows.Scan(&amp;b.Title, &amp;b.Author, &amp;b.Description)

                if err != nil{
                log.Fatal(err)
            }
                apps = append(apps, b)
            }

            render(w, &quot;myapps.html&quot;, map[string]interface{}{&quot;apps&quot; : apps})

        db.Close()
}

My main function:

func main() {

	http.HandleFunc(&quot;/myapps&quot;, myappsHandler)
	http.ListenAndServe(&quot;:8080&quot;, nil)
}

And this is myapps.html

&lt;html&gt;
&lt;body&gt;
&lt;table class=&quot;table&quot;&gt;
  &lt;tr&gt;
    &lt;th&gt;T&#237;tulo&lt;/th&gt;
    &lt;th&gt;Imagen&lt;/th&gt;
    &lt;th&gt;Descripci&#243;n&lt;/th&gt;
  &lt;/tr&gt;
{{ range .}}
  &lt;tr&gt;
    &lt;td&gt;{{ .Title }}&lt;/td&gt;
    &lt;td&gt;{{ .Author }}&lt;/td&gt;
    &lt;td&gt;{{ .Description }}&lt;/td&gt;
    &lt;td&gt; &lt;form action=&quot;/delete&quot;&gt;
          &lt;p class=&quot;navbar-form navbar-right&quot;&gt;&lt;button type=&quot;submit&quot; class=&quot;btn btn-danger&quot;&gt;Borrar&lt;/button&gt; &lt;/p&gt;
       &lt;/form&gt;&lt;/td&gt;
  &lt;/tr&gt;
{{ end  }}
&lt;/table&gt;
    &lt;/body&gt;
&lt;/html&gt;

答案1

得分: 2

你的render函数接受一个http.ResponseWriter和一个string参数。你试图传递给它一个http.ResponseWriter、一个string和一个[]App

简短的答案是,你应该将函数改为接受一个map[string]interface{}参数,然后将其传递给ExecuteTemplate函数。当你想要在模板中传递多个值时,这将非常有用。

func render(w http.ResponseWriter, tmpl string, data map[string]interface{}) {
    tmpl = fmt.Sprintf("templates/%s", tmpl)
    t, err := template.ParseFiles(tmpl)
    if err != nil {
        log.Print("template parsing error: ", err)
    }
    // 将数据传递给模板
    err = t.Execute(w, data)
    if err != nil {
        log.Print("template executing error: ", err)
    }
}

然后这样调用函数:

render(w, "myapps.html", map[string]interface{}{
    "apps": apps,
})

长答案是:

  • 你在每个请求中都建立了一个新的数据库连接池。这样做不好!要么创建一个全局连接池(可以的;sql.DB有锁),要么将*sql.DB传递给处理程序。

  • 你在每个请求中都重新解析模板。这样做很慢且效率低下。按照 http://golang.org/doc/articles/wiki/#tmp_6 的说明,在启动时只需解析一次模板即可。

  • 你可以通过编写type M map[string]interface{}来为map[string]interface{}写一个快捷方式,这样你就可以写成render(w, "myapps.html", M{"apps": apps})

  • 你应该将数据库查询拆分为一个单独的函数,以便在其他地方可以重用它。

  • sqlx可以帮助你避免手动调用rows.Scan(),它是database/sql的一个方便的包装器。

更新

我在我的机器上成功编译了你的代码(大部分),并且没有发生任何panic。我建议你阅读整个堆栈跟踪,包括空指针解引用/内存地址上面的部分,可能是类似于"template parsing error"(你的文件名是否正确?)的内容。

http://play.golang.org/p/QZ65eP-Aln(我已经注释掉了数据库相关的内容)

但你需要修复模板中的{{ range .apps }},然而这不会导致panic。

<html>
<body>
<table class="table">
  <tr>
    <th>标题</th>
    <th>图片</th>
    <th>描述</th>
  </tr>
{{ range .apps }}
  <tr>
    <td>{{ .Title }}</td>
    <td>{{ .Author }}</td>
    <td>{{ .Description }}</td>
    <td> <form action="/delete">
          <p class="navbar-form navbar-right"><button type="submit" class="btn btn-danger">删除</button> </p>
       </form></td>
  </tr>
{{ end  }}
</table>
    </body>
</html>
英文:

Your render function accepts a http.ResponseWriter and a string. You are trying to pass it a http.ResponseWriter, a string and a []App.

The short answer is that you should change your function to accept a map[string]interface that you can pass to ExecuteTemplate. This is useful when you want to pass more than one thing to a template later.

func render(w http.ResponseWriter, tmpl string, data map[string]interface{}){
        tmpl = fmt.Sprintf(&quot;templates/%s&quot;, tmpl)
        t, err := template.ParseFiles(tmpl)
        if err != nil{
            log.Print(&quot;template parsing error: &quot;, err)
        }
        // We pass our data map to the template
        err = t.Execute(w, data)
        if err != nil{
            log.Print(&quot;template executing error: &quot;, err)
        }
    }

... and call it like so:

    render(w, &quot;myapps.html&quot;, map[string]interface{}{
        &quot;apps&quot;: apps
    })

The long answer is that:

  • You are establishing a new database pool on every request. Not good! Create either a global pool (okay; sql.DB has locks) or by passing a *sql.DB to your handlers.

  • You are re-parsing your templates on every request. This is slow and inefficient. Parse them once on start-up as per http://golang.org/doc/articles/wiki/#tmp_6

  • You can write a shortcut for map[string]interface{} by writing out type M map[string]interface{}, which will allow you to instead write render(w, &quot;myapps.html&quot;, M{&quot;apps&quot;: apps})

  • You should split your DB query out into its own function so you can re-use it elsewhere.

  • sqlx can help you from having to rows.Scan() things yourself - it's a nice convenience wrapper around database/sql.

Updated

I've managed to compile your code (for the most part) on my machine, and don't have any panics. I suggest you read the whole stack trace, including the bit above the nil pointer deference/memory address, which may be something along the lines of "template parsing error" (is your filename correct?).

http://play.golang.org/p/QZ65eP-Aln (I've commented out the database stuff)

You will need to fix your templates to have {{ range .apps }}, however that won't be the source of the panic.

&lt;html&gt;
&lt;body&gt;
&lt;table class=&quot;table&quot;&gt;
  &lt;tr&gt;
    &lt;th&gt;T&#237;tulo&lt;/th&gt;
    &lt;th&gt;Imagen&lt;/th&gt;
    &lt;th&gt;Descripci&#243;n&lt;/th&gt;
  &lt;/tr&gt;
{{ range .apps }}
  &lt;tr&gt;
    &lt;td&gt;{{ .Title }}&lt;/td&gt;
    &lt;td&gt;{{ .Author }}&lt;/td&gt;
    &lt;td&gt;{{ .Description }}&lt;/td&gt;
    &lt;td&gt; &lt;form action=&quot;/delete&quot;&gt;
          &lt;p class=&quot;navbar-form navbar-right&quot;&gt;&lt;button type=&quot;submit&quot; class=&quot;btn btn-danger&quot;&gt;Borrar&lt;/button&gt; &lt;/p&gt;
       &lt;/form&gt;&lt;/td&gt;
  &lt;/tr&gt;
{{ end  }}
&lt;/table&gt;
    &lt;/body&gt;
&lt;/html&gt;

huangapple
  • 本文由 发表于 2014年7月10日 17:19:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/24672762.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定