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.