> Is there a way to implement Django-style template inheritance (http://www.djangobook.com/en/2.0/chapter04/#s-template-inheritance ) using the exp/template package?
>
> The Django derived template {% block %} is similar to an exp/template {{define}} and the Django base template {% block %} is similar to an exp/template {{template}}. It looks like exp/template package has the pieces required for template inheritance, but I don't see how to stitch them together.
Template sets are there for that purpose and are easy to use. I may be missing some nuance of Django inheritance but I think the power is mostly there. Inheritance as such, no, but it's not clear to that the set idea doesn't get you to the same place. The key points are:
- sets
- template invocations
- dynamic template selection
Let me explain how sets work. You've probably figured most of this out, but bear with me.
The nicest way to do this is to put your templates into a single file and use Set.Parse. If you prefer to use separate files, that's fine; just parse each one individually and then use Set.Add. You need to create a set to use nested templates.
Simple example: define a header and footer:
const headerFooter = `
{{define "header"}}This is the header.{{end}}
{{define "footer"}}This is the footer.{{end}}
`
err := set.Parse(treeTemplate) // ALWAYS CHECK ERRORS!!!!!
if err != nil {
log.Fatal("parse error:", err)
}
Now we can write any old template and use the header and footer by writing {{template "header"}} or {{template "footer"}}. You can also give a data value to pass to the template as dot: {{template "name" data}} - but this example doesn't need it.
Note that the name is a string constant. It's not an identifier. That's because it's programmatic: you could use a variable or method or field reference there if you want dynamic template selection. I think the inheritance issue you raise is solved with that technique. Also instead of a string you can just provide an expression that evaluates to a template, making it even easier to be dynamic. In other words, you could say,
{{template .T data}}
where .T is a field or method of type string or *Template. Thus you could execute a template that contains this invocation with various data items that each contain a .T field (say), and they will invoke different pieces on the fly. You could also use a variable or other argument:
{{template $template data}}
{{template $variable.NameOfTemplate data}}
{{template $variable.SomeTemplateValuedMethod data}}
and so on. It's very powerful.
Once we have that template parsed and the set parsed, we can either:
// If the template is in the set.
err = set.Execute("theTemplate", os.Stdout, data) // ALWAYS CHECK ERRORS!!!!!
if err != nil {
log.Fatal("exec error:", err)
}
or
// If the template is not in the set.
err = tmpl.ExecuteInSet(os.Stdout, data, set) // ALWAYS CHECK ERRORS!!!!!
if err != nil {
log.Fatal("exec error:", err)
}
I scream about error checking because almost every complaint I've read about templates or scanners not working would have been answered by the complainer if that person had checked and printed the errors.
You could do this the other way around, write a "main" template that has headers and footers and so on in it, and then inject the body as needed using the same mechanism. Since templates are recursive and the template invocation is programmatic, I think this gives you the power you need.
Extra example: It's not dynamic but it's pretty, so here's the tree example from exec_test.go, with more commentary.
// Tree is a binary tree.
type Tree struct {
Val int
Left, Right *Tree
}
// treeTemplate is the definition of a template to walk the tree. We construct it as a template set so it's easy to refer to itself.
// (We could instead drop the {{define "tree"}}...{{end}} and use ExecuteInSet, but sets make all this simpler to set up.)
const treeTemplate = `
{{define "tree"}}
[
{{.Val}}
{{with .Left}}
{{template "tree" .}}
{{end}}
{{with .Right}}
{{template "tree" .}}
{{end}}
]
{{end}}
`
func TestTree(t *testing.T) {
// Build a tree to print.
var tree = &Tree{
1,
&Tree{
2, &Tree{
3,
&Tree{
4, nil, nil,
},
nil,
},
&Tree{
5,
&Tree{
6, nil, nil,
},
nil,
},
},
&Tree{
7,
&Tree{
8,
&Tree{
9, nil, nil,
},
nil,
},
&Tree{
10,
&Tree{
11, nil, nil,
},
nil,
},
},
}
// Build and parse the set (of one element).
set := NewSet()
err := set.Parse(treeTemplate) // ALWAYS CHECK ERRORS!!!!!
if err != nil {
t.Fatal("parse error:", err)
}
var b bytes.Buffer
// Use set.Execute, starting with the "tree" template.
// To see the output directly, instead of &b use os.Stdout.
err = set.Execute("tree", &b, tree) // ALWAYS CHECK ERRORS!!!!!
if err != nil {
t.Fatal("exec error:", err)
}
// This hoo-hah is to make the comparison easy and clear.
stripSpace := func(r int) int {
if r == '\t' || r == '\n' {
return -1
}
return r
}
result := strings.Map(stripSpace, b.String())
const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]"
if result != expect {
t.Errorf("expected %q got %q", expect, result)
}
}
-rob
package main
import "exp/template"
import "os"
func main() {
set := template.NewSet()
child := template.New("child")
child.MustParse("<p>something</p>")
parent := template.New("parent")
parent.MustParse(`<p>{{template "child"}}</p>`)
set.Add(parent,child)
set.Execute("parent",os.Stdout,"")
}
outputs,
<p><p>something</p></p>
--
=====================
http://jessta.id.au
The first is that the Go code knows about the relationship between the derived template and the base template. I'd like to keep this knowledge in the template files.
> The philosophy behind exp/template is that you should use the template> language to do simple things and Go to do clever things. In contrast to> Django template, exp/template Templates don't even know about files.> That illustrates a fundamental difference of approach.Given the functionality in exp/template, it feels like the package should support the derived template scenario using simple template logic. I think that I might be missing something about how to use exp/template. If not, it seems that the addition of a small generic feature will enable the scenario with simple template logic.Django also has the philosophy that clever things should be done outside of the template.I don't understand the point about file system knowledge. Django's use of the file system roughly equivalent to the use of Set in exp/template.
The set is just a name space for templates. Don't get hung up on the details.
-rob
>
> There's no ruling method that covers template inheritance because that functionality is not built-in to the package.
It's closer than you suggest, and I am endeavoring to bring it closer. There are a couple of things I plan to do as a result of this discussion, but even now you can do more than you seem to realize because you can call Set.Parse multiple times on the same set.
Thus, you can have three files: a "main", a "specialization1" and a "specialization2", and then
set1 := template.NewSet()
set1.MustParseFile("main")
set1.MustParseFile("specialization1")
set2 := template.NewSet()
set2.MustParseFile("main")
set2.MustParseFile("specialization2")
That way set1 and set2 can use the same structure but have different templates for, say "header" and "footer".
I also plan to help a little more by adding the simple method Set.AddSet and am thinking about some file helpers inside the template grammar. I think phrases like "fatally flawed" are overly dramatic for a brand new package under active development.
-rob
def mustLoad(name string) *template.Set {set := template.NewSet()set.MustParseFile(name)var buf bytes.Buffererr := set.Template("base").Execute(&buf)if err != nil { panic(err) }set.MustParseFile(buf.String())return set}
for _, filename := range v {
Here is a short, simple example to demonstrate how this mechanism is expressed with templates. I am developing a website for the Shazowie Magic Shoppe. The site will have branding elements that are common to each page and different content inside the branding area. The branding skeleton is located in branding.html:
<html> <head> <title>Shazowie Magic Shoppe - {pagetitle}</title> </head> <body> <img src="/imgs/banner.jpg"> <h1>Shazowie Magic Shoppe</h1> <h2>Your one-stop shop for esoterica and some other stuff</h2> {.child} </body> </html>
I have a bunch of other pages that I need to create: home.html, faq.html, directions.html. I'd rather not have a separate copy of the branding code in each one, as that is a maintenance nightmare, and even though I am billing by the hour, I want to do the right thing. Here's what faq.html looks like:
{.parent branding.html} <div class="faq"> <span class="faqq">So, are you guys totally nerdy or what?</span> <span class="faqa">NO! For the last time, <i>magic is for cool kids</i>.</span> </div>
In the Google AppEngine for Go code for this application, I only have to worry about loading the template for the page; the loading, parsing and execution of the parent template is handled automatically.
In other words, you could say,
{{template .T data}}
where .T is a field or method of type string or *Template. Thus you could execute a template that contains this invocation with various data items that each contain a .T field (say), and they will invoke different pieces on the fly. You could also use a variable or other argument:
{{template $template data}}
{{template $variable.NameOfTemplate data}}
{{template $variable.SomeTemplateValuedMethod data}}
and so on. It's very powerful.
Russ