douzuo5504 2016-04-03 11:18
浏览 155
已采纳

在golang模板中访问引用对象的属性(Google App引擎)

I have a data model Sections:

type Sections struct{
    SectionName     string
    IsFather        bool
    ParentSection   *datastore.Key
}

I pass sections as value to golang template and I want to get ParentSection name ParentSection.SectionName so how can I do this from template like jinja2 in python {{ParentSection.get().SectionName}}?

  • 写回答

1条回答 默认 最新

  • dtrn74832 2016-04-04 08:08
    关注

    The html/template package is not "appengine-aware", it does not know about the GAE platform, and it does not support automatic resolution of such references.

    By design philosophy, templates should not contain complex logic. If something is (or looks) too complex in templates, it should be implemented in functions. And you may register your custom functions with the Template.Funcs() method which you can call from templates.

    For your use case I recommend the following custom function which loads a Sections by its key:

    func loadSections(ctx appengine.Context, k *datastore.Key) (*Sections, error) {
        s := Sections{}
        err := datastore.Get(ctx, k, &s)
        return &s, err
    }
    

    Note that you need the Context for loading entities from the Datastore, so you have to make it available too in the template params. So your template params may look like this:

    ctx := appengine.NewContext(r)
    
    m := map[string]interface{}{
        "Sections": s, // A previously loaded Sections
        "Ctx":      ctx,
    }
    

    And by registering and using this function, you can get what you want:

        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)
    

    Now let's say you have a parent Sections whose name is "parSecName", and you have a child Sections whose name is "childSecName", and child's ParentSection points to the parent's Sections. Executing the above template you'll see this result:

    my section name: childSecName,
    parent section name: parSecName
    

    Complete example

    See this complete working example. Note: it is for demonstration purposes only, it is not optimized for production.

    It registers the /put path to insert 2 Sections. And you may use any other path to execute the template. So test it like this:

    First insert 2 Sections:

    http://localhost:8080/put
    

    Then execute and view template result:

    http://localhost:8080/view
    

    Complete runnable code:

    // +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
    }
    

    Some notes

    This child-parent relation can be modeled with the Key itself as a key may optionally contain a parent Key.

    If you don't want to "store" the parent Key in the entity's key itself, it may also be enough to just store either the key's name or the key's ID (depending on what you use), as from that the key can be constructed.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?