访问 golang 模板中引用对象的属性(Google 应用引擎)
access properties of referenced objects in golang template (Google app engine)
我有一个数据模型Sections:
type Sections struct{
SectionName string
IsFather bool
ParentSection *datastore.Key
}
我将部分作为值传递给 golang 模板,我想获得 ParentSection
名称 ParentSection.SectionName
那么我如何从 python 中的 jinja2
这样的模板中执行此操作 {{ParentSection.get().SectionName}}
?
html/template
包不是"appengine-aware",它不了解GAE平台,不支持自动解析此类引用。
根据设计理念,模板不应包含复杂的逻辑。如果某些东西(或看起来)在模板中过于复杂,则应该在函数中实现。您可以使用可以从模板调用的 Template.Funcs()
方法注册您的自定义函数。
对于您的用例,我推荐以下自定义函数,它通过键加载 Sections
:
func loadSections(ctx appengine.Context, k *datastore.Key) (*Sections, error) {
s := Sections{}
err := datastore.Get(ctx, k, &s)
return &s, err
}
请注意,您需要 Context
才能从数据存储区加载实体,因此您也必须在模板参数中使其可用。所以你的模板参数可能看起来像这样:
ctx := appengine.NewContext(r)
m := map[string]interface{}{
"Sections": s, // A previously loaded Sections
"Ctx": ctx,
}
并且通过注册和使用这个功能,你可以得到你想要的:
t := template.Must(template.New("").Funcs(template.FuncMap{
"loadSections": loadSections,
}).Parse(`my section name: {{.Sections.SectionName}},
parent section name: {{(loadSections .Ctx .Sections.ParentSection).SectionName}}`))
t.Execute(w, m)
现在假设您有一个 parent Sections
的名字是 "parSecName"
,并且您有一个 child Sections
的名字是 "childSecName"
,child的ParentSection
指向parent的Sections
。执行上面的模板你会看到这个结果:
my section name: childSecName,
parent section name: parSecName
完整示例
查看这个完整的工作示例。注意:它仅用于演示目的,并未针对生产进行优化。
注册/put
路径插入2Sections
。您可以使用任何其他路径来执行模板。所以像这样测试它:
先插入2 Sections
:
http://localhost:8080/put
然后执行查看模板结果:
http://localhost:8080/view
完整的可运行代码:
// +build appengine
package gplay
import (
"appengine"
"appengine/datastore"
"html/template"
"net/http"
)
func init() {
http.HandleFunc("/put", puthandler)
http.HandleFunc("/", myhandler)
}
func myhandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
s := Sections{}
if err := datastore.Get(ctx, datastore.NewKey(ctx, "Sections", "", 2, nil), &s); err != nil {
panic(err)
}
m := map[string]interface{}{
"Sections": s,
"Ctx": ctx,
}
t := template.Must(template.New("").Funcs(template.FuncMap{
"loadSections": loadSections,
}).Parse(`my section name: {{.Sections.SectionName}},
parent section name: {{(loadSections .Ctx .Sections.ParentSection).SectionName}}`))
t.Execute(w, m)
}
func loadSections(ctx appengine.Context, k *datastore.Key) (*Sections, error) {
s := Sections{}
err := datastore.Get(ctx, k, &s)
return &s, err
}
func puthandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
s := Sections{"parSecName", false, nil}
var k *datastore.Key
var err error
if k, err = datastore.Put(ctx, datastore.NewKey(ctx, "Sections", "", 1, nil), &s); err != nil {
panic(err)
}
s.SectionName = "childSecName"
s.ParentSection = k
if _, err = datastore.Put(ctx, datastore.NewKey(ctx, "Sections", "", 2, nil), &s); err != nil {
panic(err)
}
}
type Sections struct {
SectionName string
IsFather bool
ParentSection *datastore.Key
}
一些笔记
这个 child-parent 关系可以用 Key
本身建模,因为键可以选择包含 parent Key
.
如果您不想 "store" 实体密钥本身中的 parent 密钥,也可能只存储密钥的名称或密钥的 ID(取决于什么你使用),因为可以构造密钥。
我有一个数据模型Sections:
type Sections struct{
SectionName string
IsFather bool
ParentSection *datastore.Key
}
我将部分作为值传递给 golang 模板,我想获得 ParentSection
名称 ParentSection.SectionName
那么我如何从 python 中的 jinja2
这样的模板中执行此操作 {{ParentSection.get().SectionName}}
?
html/template
包不是"appengine-aware",它不了解GAE平台,不支持自动解析此类引用。
根据设计理念,模板不应包含复杂的逻辑。如果某些东西(或看起来)在模板中过于复杂,则应该在函数中实现。您可以使用可以从模板调用的 Template.Funcs()
方法注册您的自定义函数。
对于您的用例,我推荐以下自定义函数,它通过键加载 Sections
:
func loadSections(ctx appengine.Context, k *datastore.Key) (*Sections, error) {
s := Sections{}
err := datastore.Get(ctx, k, &s)
return &s, err
}
请注意,您需要 Context
才能从数据存储区加载实体,因此您也必须在模板参数中使其可用。所以你的模板参数可能看起来像这样:
ctx := appengine.NewContext(r)
m := map[string]interface{}{
"Sections": s, // A previously loaded Sections
"Ctx": ctx,
}
并且通过注册和使用这个功能,你可以得到你想要的:
t := template.Must(template.New("").Funcs(template.FuncMap{
"loadSections": loadSections,
}).Parse(`my section name: {{.Sections.SectionName}},
parent section name: {{(loadSections .Ctx .Sections.ParentSection).SectionName}}`))
t.Execute(w, m)
现在假设您有一个 parent Sections
的名字是 "parSecName"
,并且您有一个 child Sections
的名字是 "childSecName"
,child的ParentSection
指向parent的Sections
。执行上面的模板你会看到这个结果:
my section name: childSecName,
parent section name: parSecName
完整示例
查看这个完整的工作示例。注意:它仅用于演示目的,并未针对生产进行优化。
注册/put
路径插入2Sections
。您可以使用任何其他路径来执行模板。所以像这样测试它:
先插入2 Sections
:
http://localhost:8080/put
然后执行查看模板结果:
http://localhost:8080/view
完整的可运行代码:
// +build appengine
package gplay
import (
"appengine"
"appengine/datastore"
"html/template"
"net/http"
)
func init() {
http.HandleFunc("/put", puthandler)
http.HandleFunc("/", myhandler)
}
func myhandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
s := Sections{}
if err := datastore.Get(ctx, datastore.NewKey(ctx, "Sections", "", 2, nil), &s); err != nil {
panic(err)
}
m := map[string]interface{}{
"Sections": s,
"Ctx": ctx,
}
t := template.Must(template.New("").Funcs(template.FuncMap{
"loadSections": loadSections,
}).Parse(`my section name: {{.Sections.SectionName}},
parent section name: {{(loadSections .Ctx .Sections.ParentSection).SectionName}}`))
t.Execute(w, m)
}
func loadSections(ctx appengine.Context, k *datastore.Key) (*Sections, error) {
s := Sections{}
err := datastore.Get(ctx, k, &s)
return &s, err
}
func puthandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
s := Sections{"parSecName", false, nil}
var k *datastore.Key
var err error
if k, err = datastore.Put(ctx, datastore.NewKey(ctx, "Sections", "", 1, nil), &s); err != nil {
panic(err)
}
s.SectionName = "childSecName"
s.ParentSection = k
if _, err = datastore.Put(ctx, datastore.NewKey(ctx, "Sections", "", 2, nil), &s); err != nil {
panic(err)
}
}
type Sections struct {
SectionName string
IsFather bool
ParentSection *datastore.Key
}
一些笔记
这个 child-parent 关系可以用 Key
本身建模,因为键可以选择包含 parent Key
.
如果您不想 "store" 实体密钥本身中的 parent 密钥,也可能只存储密钥的名称或密钥的 ID(取决于什么你使用),因为可以构造密钥。