template inheritance using exp/template?

768 views
Skip to first unread message

Islan Dberry

unread,
Jul 11, 2011, 5:47:48 PM7/11/11
to golan...@googlegroups.com
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.




Fabian Reinartz

unread,
Jul 11, 2011, 6:09:23 PM7/11/11
to golan...@googlegroups.com
I don't know the syntax of the exp/template package but if inheritance is not a builtin feature you could write a small wrapper which composes numerous snippets to a final templates you Parse in the end.
For that purpose you could use a few custom keywords you use in your template files which are used by the wrapper.

Rob 'Commander' Pike

unread,
Jul 11, 2011, 6:19:06 PM7/11/11
to golan...@googlegroups.com

On 12/07/2011, at 7:47 AM, Islan Dberry wrote:

> 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

Jessta

unread,
Jul 11, 2011, 6:28:58 PM7/11/11
to golan...@googlegroups.com

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

Islan Dberry

unread,
Jul 11, 2011, 7:24:56 PM7/11/11
to golan...@googlegroups.com
Thank you for the detailed response. 

I want to define a base template with blocks injected from the derived template. Here's what I have so far:

func example1() {
    base := `<html>
</head><title>{{template "Title"}}</title></head>
<body>{{template "Body" .}}</body>
<html>`

    derived := `
{{define "Title"}}Home{{end}}
{{define "Body"}}{{.Message}}{{end}}
`
    b := template.New("x").MustParse(base)
    d := template.NewSet().MustParse(derived)
    data := struct{ Message string }{"Hello World"}
    if err := b.ExecuteInSet(os.Stdout, data, d); err != nil {
        log.Fatal("execute error:", err)
    }
}

There are a few things that I don't like about what I have so far. 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 second is that I want the derived template to have direct access to the data argument to ExecuteInSet, not the base template.

I think I can get closer to what I want if I can assign to a variable with the value of a template fragment expansion. In the example below, I use {{assign}} for this proposed feature:  

func example1() {
    base := `{{define "base"}}<html>
</head><title>{{if $title}}{{$title}}{{else}}Default Title{{end}}<title>
<body>{{if $body}}{{$body}}{{end}}</body>
<html>{{end}}`

    derived := `
{{assign $title}}Home{{end}}
{{assign $body}}The message is {{.Message}}{{end}}
{{template "base"}}
`
    baseSet := template.NewSet().MustParse(base)
    derivedTemplate := template.New().MustParse(derived)
    data := struct{ Message string }{"Hello World"}
    if err := derivedTemplate.ExecuteInSet(os.Stdout, data, baseSet); err != nil {
        log.Fatal("execute error:", err)
    }
}

If I load the base set with all of the base templates used in my application, then the Go code does not need to know how the page specific templates are derived or if the page specific templates even use derivation.

As an added bonus, the derived templates can provide default blocks using the {{if}} {{else}} {{end}} syntax.

The variable scoping is not ideal in my example. I don't want base templates to see all of the variables that I assign in a derived template.

Is there a way to do this without my proposed {{assign}} feature?

Andrew Gerrand

unread,
Jul 11, 2011, 7:34:00 PM7/11/11
to golan...@googlegroups.com
On Tuesday, 12 July 2011 09:24:56 UTC+10, Islan Dberry wrote:
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.

Ultimately, it's all code. The real issue is you don't want certain parts of your code to know about these layout concerns... so encapsulate them! Write some functions (or even a package) to do the template wrangling, and then the rest of your application code won't need to know about the templates and how they fit together.

Andrew

Islan Dberry

unread,
Jul 11, 2011, 9:00:14 PM7/11/11
to golan...@googlegroups.com
> 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 real issue is you don't want certain parts of your code to know
> about these layout concerns.

That's one issue. The other issue is that I want to keep the layout concerns in one place. If I write a template that's derived from some other template, I'd like to say that in the template file.

Andrew Gerrand

unread,
Jul 11, 2011, 9:34:43 PM7/11/11
to golan...@googlegroups.com
On Tuesday, 12 July 2011 11:00:14 UTC+10, Islan Dberry wrote:
> 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.

It's not quite the same.

In exp/template, sets contain templates. Templates can include other templates from the set by name. Templates can also be used outside of the context of a set.

In Django, templates can contain blocks. Templates can inherit from other templates by file name, and they do so by naming the parent file and declaring a subset of the blocks present in the parent template.

The two systems are not analogous. To bridge the gap you'll need to write some Go code. I'm have a couple of ideas as to how a helper might make this easier.

Andrew

Islan Dberry

unread,
Jul 11, 2011, 10:29:18 PM7/11/11
to golan...@googlegroups.com
Here's my new idea for implementing derived templates. A derived template is a set where the special "base" template in the set evaluates to the name of the base template:

{{define "base"}}site.html{{end}}
{{define "Title"}}Home{{end}}
{{define "Body"}}{{.Message}}{{end}}

The wrapper code for evaluating the template is:

func executeDerived(w io.Writer, derived *template.Set, baseSet *template.Set, data interface{}) os.Error {
for name, template := range set.Map()  {
   var baseName string
   var m map[string][]byte
   var buf bytes.Buffer
   if err := template.Execute(&b, data); err != nil {
     return err
   }
   if name == "base" {
      baseName = buf.String()
   } else {
      m[name] = buf.Bytes()
  }
  return baseSet.Template(baseName).Execute(w, m)
}

Here's the template in the base set for this example:

{{define "site.html"}}<html>
</head><title>{{if title}}{{title}}{{else}}Default Title{{end}}<title>
<body>{{if body}}{{body}}{{end}}</body>
<html>{{end}}

This needs some cleanup, but I think it does what I want. The {{define base}} {{end}} feels hacky, but I can live with it. I can probably eliminate the use of the temporary buffer in 
executeDerived.

>> Django's use of the file system roughly equivalent to the use
>> of Set in exp/template.
It's not quite the same.

Django uses the file system to map strings to templates. The exp/template package uses Sets to map strings to templates.  Same basic idea.

Islan Dberry

unread,
Jul 11, 2011, 10:38:59 PM7/11/11
to golan...@googlegroups.com
The for loop should be

for name, template := range derived.Map()  {

where Map() is a new method on Set.

Islan Dberry

unread,
Jul 12, 2011, 3:11:33 PM7/12/11
to golan...@googlegroups.com
Here's another attempt at template inheritance: https://gist.github.com/1078706

The "main" template in a derived set is executed with a wrapper around the application data and the derived set.  The "main" template invokes the base template and passes in the wrapper.  The base template accesses templates and application data through the wrapper.

The base templates look a little clunky.

It does not compose.  A base template cannot be a derived template.

The example puts everything in a single file. In actual use, the templates will be loaded from files and a helper function will be added for executing derived templates.

Still digging. 

George Nava

unread,
Jul 12, 2011, 6:57:01 PM7/12/11
to golang-nuts
Guys please forgive me but I have to ask a question, first some
background: in python/django we just do
"template.render('mycomplexpage',data)" and that's it, the template
parser knows if it is a simple or derived template and includes them
accordingly. Again, the parser must know, not the developer. You know,
separation of concerns, developers and designers, each to his own. My
program should be able to render that page if it is one single page or
a multi-template page without having to change one line of code. I
don't mean we should just copy django, just adding some context to the
issue.

Now, I've been reading the exp/template specs and code, and while I
like all the powerful features included, all I see is handling sets
and complex stuff like that for multi-template rendering.

Now the question: will my code remain unchanged even if the designer
decides to use one or many templates, or am I going to have to change
my code everytime he decides to reorganize headers and footers and
sections and articles and stuff?

The former would be the expected answer, the latter will kill all
possible frameworks for Go in the spot.

Rob 'Commander' Pike

unread,
Jul 12, 2011, 7:36:59 PM7/12/11
to George Nava, golang-nuts
If your framework assembles the templates into a set, which it should do, all will be well.

The set is just a name space for templates. Don't get hung up on the details.

-rob

Islan Dberry

unread,
Jul 12, 2011, 8:29:17 PM7/12/11
to golan...@googlegroups.com
> will my code remain unchanged even if the designer decides to use
> one or many templates

If your application creates a template set from all of the files in a file system directory, then the designer can add and remove templates by adding and removing files from the directory. No code changes needed.

You will need to change code if the designer needs to go beyond what's expressible in the template language.

the latter will kill all possible frameworks for Go in the spot

Because it's easy to use third party template packages, we will survive even if the standard template package is fatally flawed.

Backpack

unread,
Jul 12, 2011, 9:03:05 PM7/12/11
to golang-nuts
I know there will be workarounds to make the final implementation
work, we may pass a slice of templates to the render method and from
there, if it is just one, render it normally, else use a set. We as
programmers will always find solutions to every problem, thing is,
shouldn't we start it all from the simple premise that
"template.render('mycomplexpage',data)" should work all the time, no
matter how complex the template or the data?

Now the question as an user would be: one method to rule them all?

template.render(T{"page"},data)
template.render(T{"hdr","bdy","ftr"},data)

or two separate methods?

template.render("pagename",data)
template.renderSet("setname",data)

Islan Dberry

unread,
Jul 12, 2011, 10:20:38 PM7/12/11
to golan...@googlegroups.com
> Now the question as an user would be: one method to rule them all? 

If you work with the functionality that's built-in to exp/package, then the method is http://tip.goneat.org/pkg/exp/template/#Set.Execute .

The app assembles all templates into a set a startup time. The app executes the root level template by name using Set.Execute. The root template can include other templates without writing code.

There's no ruling method that covers template inheritance because that functionality is not built-in to the package.

Rob 'Commander' Pike

unread,
Jul 12, 2011, 10:34:16 PM7/12/11
to golan...@googlegroups.com

On 13/07/2011, at 12:20 PM, Islan Dberry wrote:

>
> 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

Islan Dberry

unread,
Jul 12, 2011, 11:59:05 PM7/12/11
to golan...@googlegroups.com
Sorry. I was trying to point out that it's overly dramatic to say that a template package can kill all frameworks for Go. I didn't mean to imply that exp/template is fatally flawed. It's not. The reason that I am yakking about the package is that I like it and hope to use it. 

The approach of calling Set.Parse multiple times is interesting. I can use a template with a well known name in the derived set to discover the name of the base set:

def mustLoad(name string) *template.Set {
   set := template.NewSet()
   set.MustParseFile(name)
   var buf bytes.Buffer
   err := set.Template("base").Execute(&buf)
   if err != nil { panic(err) }
   set.MustParseFile(buf.String())
   return set
}

There are a couple of issues. The first is that the derived template should have control over the data passed to the base template. In this scheme, the base template gets first crack at the data. My proposals have this same problem.

The second issue is supporting multiple levels of derivation. I can modify the mustLoad function above to be recursive, but the templates for all levels of the derivation end up in the same flat name space. Template designers will need to carefully manage the names of templates to avoid conflicts between different levels of the derivation.

I look forward to seeing your new additions. 

Andrew Gerrand

unread,
Jul 13, 2011, 1:31:39 AM7/13/11
to golan...@googlegroups.com
On Wednesday, 13 July 2011 13:59:05 UTC+10, Islan Dberry wrote:
def mustLoad(name string) *template.Set {
   set := template.NewSet()
   set.MustParseFile(name)
   var buf bytes.Buffer
   err := set.Template("base").Execute(&buf)
   if err != nil { panic(err) }
   set.MustParseFile(buf.String())
   return set
}
 
I think we all recognize this as a gross hack. Why not store the configuration of the templates in a configuration file (as JSON)?

{
  "frontpage": ["main.set", "frontpage.set"],
  "article": ["main.set", "article.set"],
}

Then read the data into a value of this type:

type SetConfig map[string][]string

Then set up the sets this way:

func ReadSets(config *SetConfig) map[string]*template.Set {
   sets := make(map[string]*template.Set)
   for name, setlist := range config {
      set := template.NewSet()
      for _, filename := range v {
         set.MustParseFile(filename)
      }
      sets[name] = set
   }
   return sets
}

Pretty easy, and doesn't require putting weird semantics into the template definitions themselves.

Andrew

Andrew Gerrand

unread,
Jul 13, 2011, 1:32:39 AM7/13/11
to golan...@googlegroups.com
On Wednesday, 13 July 2011 15:31:39 UTC+10, Andrew Gerrand wrote:
      for _, filename := range v {

s/v/setlist/ 

adam.cr...@gmail.com

unread,
Aug 2, 2011, 1:49:26 PM8/2/11
to golan...@googlegroups.com
I have just released some very simple changes to the stock template package that implement a simple kind of template inheritance. It's not quite like Django in that it doesn't support {block}s, rather it simply provides for embedding a child template inside a parent template.

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.htmlfaq.htmldirections.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.



Fabian Reinartz

unread,
Aug 4, 2011, 2:18:37 PM8/4/11
to Rob 'Commander' Pike, golan...@googlegroups.com
2011/7/12 Rob 'Commander' Pike <r...@google.com>
 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.

 For some reasons this doesn't work for me. My program exits with:

"template: base.html:17: unexpected ".Temp" in template invocation"

My dot is a  struct with Temp as a string. The error appears immediatly - so no execution of a template is needed to cause the error. I also tried the second example with a variable but the result is the same. Any ideas?

Russ Cox

unread,
Aug 4, 2011, 2:40:34 PM8/4/11
to Fabian Reinartz, Rob 'Commander' Pike, golan...@googlegroups.com
The mail you quoted was written July 12.
The template rules have changed.
Now the syntax is {{template "name"}}
and the name must be a literal string,
not a variable. This is so that an analysis
of the template can always resolve call
sites to the subtemplates being invoked.

Russ

Fabian Reinartz

unread,
Aug 4, 2011, 2:44:48 PM8/4/11
to golan...@googlegroups.com
Okay thanks. I knew this mail was older but I didn't expect that the feature was removed completely.
I guess this makes inheritance less easy.
Reply all
Reply to author
Forward
0 new messages