访问 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(取决于什么你使用),因为可以构造密钥。