pongo2 Documentation

repository·master·Indexed 25 days ago

https://github.com/flosch/pongo2

A Go templating language with syntax and feature compatibility for Django templates. pongo2 supports advanced C-like expressions, complex function calls, and template sandboxing. It provides capabilities for rendering templates from strings or files, implementing custom tags and filters, and managing execution contexts for scoped data and error handling.

Tokens
25.8K
Snippets
78
Records
138
Agent score
82%

What's inside pongo2

  1. Control Output (autoescape, filter, verbatim, etc.)

    master

    Manipulate how content is rendered and escaped.

    • {% autoescape on|off %} / {% endautoescape %}: Controls automatic HTML escaping.
    • {% filter filter_name %} / {% endfilter %}: Applies one or more filters to an entire block of content.
    • {% spaceless %} / {% endspaceless %}: Removes whitespace between HTML tags.
    • {% verbatim %} / {% endverbatim %}: Outputs content without processing any template syntax (useful for client-side frameworks like Vue or Angular).
    • {% comment %} / {% endcomment %}: Multi-line comments that are not rendered.
    {% verbatim %}
      {{ this_will_not_be_processed }}
    {% endverbatim %}
  2. Implement a Block Tag

    master

    A block tag wraps content between an opening and a closing tag (e.g., {% uppercase %}...{% enduppercase %}). Use doc.WrapUntilTag in your parser to capture the content into a *pongo2.NodeWrapper. In the Execute method, you can execute the wrapper to capture its output into a buffer for transformation.

    type tagUppercaseNode struct {
        wrapper *pongo2.NodeWrapper
    }
    
    func (node *tagUppercaseNode) Execute(ctx *pongo2.ExecutionContext, writer pongo2.TemplateWriter) error {
        var buf bytes.Buffer
        err := node.wrapper.Execute(ctx, &buf)
        if err != nil {
            return err
        }
        writer.WriteString(strings.ToUpper(buf.String()))
        return nil
    }
    
    func tagUppercaseParser(doc *pongo2.Parser, start *pongo2.Token, arguments *pongo2.Parser) (pongo2.INodeTag, error) {
        node := &tagUppercaseNode{}
        wrapper, endArgs, err := doc.WrapUntilTag("enduppercase")
        if err != nil {
            return nil, err
        }
        node.wrapper = wrapper
        return node, nil
    }
    
    func init() {
        pongo2.RegisterTag("uppercase", tagUppercaseParser)
    }
  3. Define and call macros

    master

    Macros are reusable template fragments that act like functions. They accept arguments, can have default values, and return rendered content. Use the {% macro %} tag to define them and {{ }} to call them.

    Syntax:

    {% macro name(arg1, arg2, ...) %}
      ...content...
    {% endmacro %}

    Calling:

    {{ macro_name("arg_value") }}
    {% macro button(text, url) %}
      <a href="{{ url }}" class="btn">{{ text }}</a>
    {% endmacro %}
    
    {{ button("Click Me", "/home") }}
  4. Implement a Tag with Expressions

    master

    Tags can accept expressions (like variables or numbers) as arguments. Use arguments.ParseExpression() in the parser to obtain a pongo2.IEvaluator. During Execute, call node.countExpr.Evaluate(ctx) to resolve the expression's value at runtime.

    type tagRepeatNode struct {
        position  *pongo2.Token
        countExpr pongo2.IEvaluator
        wrapper   *pongo2.NodeWrapper
    }
    
    func (node *tagRepeatNode) Execute(ctx *pongo2.ExecutionContext, writer pongo2.TemplateWriter) error {
        countVal, err := node.countExpr.Evaluate(ctx)
        if err != nil {
            return err
        }
        count := countVal.Integer()
        for i := 0; i < count; i++ {
            if err := node.wrapper.Execute(ctx, writer); err != nil {
                return err
            }
        }
        return nil
    }
    
    func tagRepeatParser(doc *pongo2.Parser, start *pongo2.Token, arguments *pongo2.Parser) (pongo2.INodeTag, error) {
        node := &tagRepeatNode{position: start}
        countExpr, err := arguments.ParseExpression()
        if err != nil {
            return nil, err
        }
        node.countExpr = countExpr
        wrapper, _, err := doc.WrapUntilTag("endrepeat")
        if err != nil {
            return nil, err
        }
        node.wrapper = wrapper
        return node, nil
    }
    
    func init() {
        pongo2.RegisterTag("repeat", tagRepeatParser)
    }
  5. Implement custom tags in pongo2

    master

    A custom tag requires two components:

    1. Tag Parser Function: Parses the tag syntax at compile time.
    2. Tag Node: Executes the tag logic at render time.

    The parser function must match the TagParser signature, and the resulting node must implement the INodeTag interface, which includes an Execute method.

    // Tag parser function signature
    type TagParser func(doc *Parser, start *Token, arguments *Parser) (INodeTag, error)
    
    // Tag node must implement INodeTag (which extends INode)
    type INodeTag interface {
        Execute(ctx *ExecutionContext, writer TemplateWriter) error
    }
  6. Handle Execution-Time Errors in Custom Tags

    master

    During the execution phase of a tag, use the ExecutionContext.Error(msg, token) method to report runtime issues (e.g., invalid values or nil pointers). To wrap an existing Go error with template position information, use ctx.OrigError(err, token).

    func (node *myNode) Execute(ctx *pongo2.ExecutionContext, writer pongo2.TemplateWriter) error {
        val, err := node.expr.Evaluate(ctx)
        if err != nil {
            return err
        }
    
        if val.IsNil() {
            return ctx.Error("value cannot be nil", node.position)
        }
    
        if val.Integer() < 0 {
            return ctx.Error("value must be non-negative", node.position)
        }
    
        return nil
    }
    
    // Wrapping an existing error
    if err != nil {
        return ctx.OrigError(err, node.position)
    }
  7. Implement a Tag with Loop Variables

    master

    To create iteration tags (like {% each item in items %}), you must manage a child context for each iteration. Use pongo2.NewChildExecutionContext(ctx) to create a new context, then populate its Private map with the loop variable and loop metadata (e.g., eachloop).

    Loop metadata structure example:

    type eachLoop struct {
        Counter     int
        Counter0    int
        First       bool
        Last        bool
        Revcounter  int
        Revcounter0 int
    }
  8. Prevent Server-Side Template Injection (SSTI)

    master

    To prevent SSTI, never concatenate user input directly into the template string. Instead, use placeholders in the template and pass the user input via the pongo2.Context during execution.

    // SAFE
    tpl, _ := pongo2.FromString("Hello {{ name }}!")
    tpl.Execute(pongo2.Context{"name": userInput})
  9. Use Utility Tags (now, lorem, widthratio)

    master

    Access helper functions for common template tasks.

    • {% now "format" %}: Outputs the current date/time using Go's time format strings (e.g., {% now "2006-01-02" %}).
    • {% lorem %}: Generates placeholder text.
      • {% lorem 3 %}: 3 paragraphs.
      • {% lorem 5 w %}: 5 words.
      • {% lorem 2 p %}: 2 HTML paragraphs.
    • {% widthratio value max width %}: Calculates a ratio for UI elements like progress bars. Formula: (value / max_value) * max_width.
    • {% templatetag name %}: Outputs template syntax characters like {% templatetag openblock %} (outputs {%).
    <div style="width: {% widthratio current_value max_value 100 %}px;"></div>
  10. Format dates and strings using Go syntax

    master

    pongo2 uses Go's time formatting and fmt.Sprintf logic instead of Python/Django defaults.

    Date Formatting: Use the |date filter with Go's reference time (2006-01-02).

    • {{ now|date:"2006-01-02" }}
    • {{ now|date:"Monday, January 2, 2006" }}

    String Formatting: Use the |stringformat filter with Go's fmt.Sprintf verbs.

    • {{ 3.14159|stringformat:"%.2f" }}
    {{ now|date:"2006-01-02" }}
    {{ now|time:"15:04:05" }}
    {{ 3.14159|stringformat:"%.2f" }}
    {{ 42|stringformat:"%05d" }}
  11. Sandbox Templates by Banning Tags and Filters

    master

    To secure a template set (especially when handling user-provided templates), you can ban specific tags and filters.

    CRITICAL: You must call BanTag or BanFilter BEFORE loading any templates into the set. Once the first template is loaded, the set is locked, and attempting to ban items will return an error.

    Commonly banned items for security:

    • set.BanTag("include"): Prevents file inclusion.
    • set.BanTag("ssi"): Prevents server-side includes.
    • set.BanFilter("safe"): Prevents users from bypassing autoescape.
    set := pongo2.NewSet("restricted", loader)
    
    // Ban dangerous tags BEFORE loading templates
    set.BanTag("include")
    set.BanTag("ssi")
    set.BanFilter("safe")
    
    // Now it is safe to load templates
    tpl, err := set.FromFile("user-content.html")
  12. Export and import macros across templates

    master

    By default, macros are only available in the template where they are defined. To use them in other templates, you must mark them with the export keyword immediately after the argument list.

    To use exported macros, use the {% import %} tag.

    Import Syntax: {% import "filename" macro1, macro2 %}

    Aliasing: You can rename imported macros using the as keyword to avoid name conflicts or create shorter names. {% import "filename" macro_name as alias %}