Coder Social home page Coder Social logo

flosch / pongo2 Goto Github PK

View Code? Open in Web Editor NEW
2.8K 68.0 267.0 713 KB

Django-syntax like template-engine for Go

Home Page: https://www.schlachter.tech/pongo2

License: MIT License

Go 87.93% Smarty 12.03% HTML 0.05%
template go django template-engine pongo2 templates template-language golang golang-library

pongo2's Introduction

PkgGoDev Build Status Run on Repl.it

pongo2 is a Django-syntax like templating-language (official website).

Install/update using go get (no dependencies required by pongo2):

go get -u github.com/flosch/pongo2/v6

Please use the issue tracker if you're encountering any problems with pongo2 or if you need help with implementing tags or filters (create a ticket!).

Looking for a Go developer/consultant? I'm available for hire. 👨‍💻

First impression of a template

<html>
  <head>
    <title>Our admins and users</title>
  </head>
  {# This is a short example to give you a quick overview of pongo2's syntax. #}
  {% macro user_details(user, is_admin=false) %}
  <div class="user_item">
    <!-- Let's indicate a user's good karma -->
    <h2 {% if (user.karma>= 40) || (user.karma > calc_avg_karma(userlist)+5) %} class="karma-good"{% endif %}>

      <!-- This will call user.String() automatically if available: -->
      {{ user }}
    </h2>

    <!-- Will print a human-readable time duration like "3 weeks ago" -->
    <p>This user registered {{ user.register_date|naturaltime }}.</p>

    <!-- Let's allow the users to write down their biography using markdown;
             we will only show the first 15 words as a preview -->
    <p>The user's biography:</p>
    <p>
      {{ user.biography|markdown|truncatewords_html:15 }}
      <a href="/user/{{ user.id }}/">read more</a>
    </p>

    {% if is_admin %}
    <p>This user is an admin!</p>
    {% endif %}
  </div>
  {% endmacro %}

  <body>
    <!-- Make use of the macro defined above to avoid repetitive HTML code
         since we want to use the same code for admins AND members -->

    <h1>Our admins</h1>
    {% for admin in adminlist %} {{ user_details(admin, true) }} {% endfor %}

    <h1>Our members</h1>
    {% for user in userlist %} {{ user_details(user) }} {% endfor %}
  </body>
</html>

Features

Caveats

Filters

  • date / time: The date and time filter are taking the Golang specific time- and date-format (not Django's one) currently. Take a look on the format here.
  • stringformat: stringformat does not take Python's string format syntax as a parameter, instead it takes Go's. Essentially {{ 3.14|stringformat:"pi is %.2f" }} is fmt.Sprintf("pi is %.2f", 3.14).
  • escape / force_escape: Unlike Django's behaviour, the escape-filter is applied immediately. Therefore there is no need for a force_escape-filter yet.

Tags

  • for: All the forloop fields (like forloop.counter) are written with a capital letter at the beginning. For example, the counter can be accessed by forloop.Counter and the parentloop by forloop.Parentloop.
  • now: takes Go's time format (see date and time-filter).

Misc

  • not in-operator: You can check whether a map/struct/string contains a key/field/substring by using the in-operator (or the negation of it): {% if key in map %}Key is in map{% else %}Key not in map{% endif %} or {% if !(key in map) %}Key is NOT in map{% else %}Key is in map{% endif %}.

Add-ons, libraries and helpers

Official

  • pongo2-addons - Official additional filters/tags for pongo2 (for example a markdown-filter). They are in their own repository because they're relying on 3rd-party-libraries.

3rd-party

Please add your project to this list and send me a pull request when you've developed something nice for pongo2.

Who's using pongo2

I'm compiling a list of pongo2 users. Add your project or company!

API-usage examples

Please see the documentation for a full list of provided API methods.

A tiny example (template string)

// Compile the template first (i. e. creating the AST)
tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
if err != nil {
    panic(err)
}
// Now you can render the template with the given
// pongo2.Context how often you want to.
out, err := tpl.Execute(pongo2.Context{"name": "florian"})
if err != nil {
    panic(err)
}
fmt.Println(out) // Output: Hello Florian!

Example server-usage (template file)

package main

import (
    "github.com/flosch/pongo2/v6"
    "net/http"
)

// Pre-compiling the templates at application startup using the
// little Must()-helper function (Must() will panic if FromFile()
// or FromString() will return with an error - that's it).
// It's faster to pre-compile it anywhere at startup and only
// execute the template later.
var tplExample = pongo2.Must(pongo2.FromFile("example.html"))

func examplePage(w http.ResponseWriter, r *http.Request) {
    // Execute the template per HTTP request
    err := tplExample.ExecuteWriter(pongo2.Context{"query": r.FormValue("query")}, w)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func main() {
    http.HandleFunc("/", examplePage)
    http.ListenAndServe(":8080", nil)
}

pongo2's People

Contributors

aleksen avatar cam-stitt avatar d0xin avatar deroshkin avatar dieselburner avatar digitalcrab avatar flosch avatar fossabot avatar gavbaa avatar gbts avatar heavyhorst avatar hiveminded avatar hnakamur avatar iredmail avatar jclc avatar kai5263499 avatar kohkimakimoto avatar monkeywithacupcake avatar pamelin avatar pierrre avatar robvdl avatar romanodesouza avatar sokoloffa avatar spuzirev avatar stnc avatar tcyrus avatar thatguystone avatar vitalbh avatar wpjunior avatar wrouesnel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pongo2's Issues

Get value by key from map

I'm passing map[string]string to pongo2 context and want to verify if a key is set:

<div class="form-group {% if errors[`Path`] %}has-error{%endif%}">

This gives panic: 2014/07/26 21:56:39 PANIC: [Lexer Error in ./templates/grayscale/form.html (Line 70 Col 38)]: Unknown character: '[' (91)

Also tried errors.Path - does not panic, but does not check if the key is set. After key is checked and exists I want to print its value (does not work either).

Is it possible to do this with current pongo2 implementation or I need to implement filter, something like this: {{ errors|map_key:"Path" }}?

Template.Execute returns []byte

Can Template.Execute returns []byte type? When html is large, returns string will cause huge content copy, where []byte will just copy a pointer.

[missing verbatim-tag] pongo2 and the css code in single line html file will cause error

    <style type="text/css" id="adhoccss-responsive-design">#weather .ttip{width:114px;white-space:nowrap}.dailyBuzz .photo-link{float:left}.dailyBuzz .videobutton,.dailyBuzz .videobutton-bg{margin-top:-5px}#dl_v2 .dllabel,#dl_v2 #advertad1 .dllabel,#dl_v2.dl #mkplace1 .dllabel{top:0}.slideshow h3 a{font-size:15px}@media only screen and (max-width:767px){#dl_v2 .dllabel,#dl_v2 #advertad1 .dllabel,#dl_v2.dl #mkplace1 .dllabel{display:none}.sprite-down-arrow-dark,#usrMnu .userOptions:after{display:none}}@media only screen and (max-width:479px){#aol-header{min-height:82px}#aol-header-search{display:block!important}.quicknav a.mobile-search{display:none}.quicknav .mobile-search img{display:none}.SAF #aol-header.aol-global-header .quicknav{width:auto}}        #gravitymodarticle{clear:both}@media only screen and (-webkit-min-device-pixel-ratio:0) and (min-width:1024px){#dl_v2 h2{font-size:28px}#dl_v2 h2 a{font-size:28px}}#msg-ol-mtmhp .promo a.btnlbl{top:150px}#msg-ol-mtmhp .promo .promo-txt{font-size:16px}#msg-ol-mtmhp .header{padding:7px 0 7px 15px}.msgolmtmhpWr #cboxClose{top:12px}#dl_v2 .sublede-section{padding-top:9px}.rnd .aslide .logo-overlay{height:auto}.respSlideshow a.hero-slide-container .gradient{background:-moz-linear-gradient(top,rgba(0,0,0,0) 0%,rgba(0,0,0,.8) 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,rgba(0,0,0,0)),color-stop(100%,rgba(0,0,0,.8)));background:-webkit-linear-gradient(top,rgba(0,0,0,0) 0%,rgba(0,0,0,.8) 100%);background:-o-linear-gradient(top,rgba(0,0,0,0) 0%,rgba(0,0,0,.8) 100%);background:-ms-linear-gradient(top,rgba(0,0,0,0) 0%,rgba(0,0,0,.8) 100%);background:linear-gradient(to bottom,rgba(0,0,0,0) 0%,rgba(0,0,0,.8) 100%);filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=0);position:absolute;bottom:0;left:0;width:100%;height:45%}#network-banner #banner-close{margin-left:0;right:10px;width:12px}#network-banner .banner-inner{width:100%}#rr-search-tn .trending-now{width:100%!important}#rr-search-tn .trending-now li{width:49.5%;float:left;padding:5px 0 5px 0}#rr-search-tn .trending-now ol{font-size:12px;list-style-type:none;width:100%!important;margin-top:10px}#rr-search-tn .trending-now .down,.trending-now .up{background-image:url("/img/xsprite.png.pagespeed.ic.BV9PsbFClJ.png");background-repeat:no-repeat;height:16px;position:relative;width:20px;padding-left:14px}#rr-search-tn .trending-now .down{background-position:0 -2657px}#rr-search-tn .trending-now .up{background-position:0 -1305px}#rr-search-tn .trending-now .down img,.trending-now .up img{display:none}#rr-search-tn .trending-now ol.nrml{font-weight:normal}#rr-search-tn .trending-now li a.num,#rr-search-tn .trending-now li a.noicon,#rr-search-tn .trending-now li a.down,#hotsearchmod .trending-now li a.up{float:left;padding-top:4px;padding-bottom:4px}#rr-search-tn .trending-now a.up,#hotsearchmod .trending-now a.down{display:list-item}#rr-search-tn .trending-now li a{padding-left:16px}#rr-search-tn .trending-now a .sprite{overflow:hidden;top:0;position:relative;display:block;width:16px;height:10px;float:left;margin-left:-16px}#rr-search-tn .trending-now a .sprite img{position:relative}#rr-search-tn .trending-now a .down img{left:0;top:-716px}#rr-search-tn .trending-now a .up img{left:0;top:-677px}#rr-search-tn .trending-now a.num .sprite,#rr-search-tn .trending-now a.bullet .sprite,#rr-search-tn .trending-now a.noicon .sprite{display:none}#rr-search-tn .trending-now a.bullet::before{content:"\2022";padding-right:5px;margin-left:-15px}#rr-search-tn .trending-now a.noicon{padding-left:0}#rr-search-tn .trending-now a.bullet,#rr-search-tn .trending-now a.num{display:list-item}#rr-search-tn .trending-now a.num i{font-style:normal;width:15px;display:inline-block;text-align:right;margin-left:-19px}#rr-search-tn .trending-now a.num:hover i{color:#000}@media only screen and (max-width:480px){#dl_v2 h2,#dl_v2 h2 a{width:105%}}#aol-standalonesearch-query{width:72%;padding:5px 0 5px 9px;line-height:12px;font-size:12px;height:18px}#aol-standalonesearch-search-button{border:medium none;color:#fff;font-size:13px;font-weight:400;line-height:12px;margin:0;height:32px;width:22%}#aol-standalonesearch-search{margin-bottom:30px}
    </style>
    <style type="text/css" id="cobrandadhoccss-with-inheritance"></style>
    <style>#rr-search-tn{margin-bottom:-7px}</style>
    <link rel="canonical" href="index.html"/>
panic: [Lexer Error in templates\head.html (Line 8 Col 356)]: Newline not permitted in a single-line comment.

Pongo comment tags and CSS named ID style conflict, lead to match error, and behind all the HTML tags are comment code.

Filter and multibyte characters

Example:

{{ "выход"|capfirst }} -> Ð�ыход

strings (t[0], t[1:]) is slicing the byte, use rune

func filterCapfirst2(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, error) {

    if in.Len() <= 0 {

        return pongo2.AsValue(""), nil
    }

    t := in.String()

    r := []rune(t)

    return pongo2.AsValue(strings.ToUpper(string(r[0])) + string(r[1:])), nil
}

pongo2.RegisterFilter("capfirst2", filterCapfirst2)

{{ "выход"|capfirst2 }} -> Выход

but all the filters do not work with multibyte characters

Create more detailed documentation

Including:

  • Doc of pongo2 filters and tags (regular ones + pongo2-specific ones like set, import, ...); both from pongo2 and pongo2-
  • Macro imports/exports and macros calling macros.
  • Template sets
    • Sandboxing (directory patterns, banning of filters/tags)
  • ... (any other ideas?)

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/4462317-create-more-detailed-documentation?utm_campaign=plugin&utm_content=tracker%2F3654947&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F3654947&utm_medium=issues&utm_source=github).

Allow sandboxed mode

Sandbox mode includes:

  • Limited directory access
  • Restricted subset of tags/filters

Unimplemented filter usage gives not clear panic message

I got panic message while template execution:

2014/07/22 04:07:25 PANIC: runtime error: invalid memory address or nil pointer dereference
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/panic.c:552 (0x10d2d)
    panicstring: runtime·panic(err);
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/os_darwin.c:454 (0xf9ce)
    sigpanic: runtime·panicstring("invalid memory address or nil pointer dereference");
/Users/vgarvardt/Sites/blogo/.godeps/src/github.com/flosch/pongo2/template.go:66 (0x126080)
    (*Template).execute: panic(string(tpl.size))
/Users/vgarvardt/Sites/blogo/.godeps/src/github.com/flosch/pongo2/template.go:133 (0x126474)
    (*Template).Execute: buffer, err := tpl.execute(context)
...

I found that panic occurs when I used join filter that is not implemented yet. It would be much easier to debug templates when panic can give more informative messages, e.g. "unknown filter found 'join'".

PS: btw, thanks for the project - even without lots of filters and tags implemented it makes my life much easier =)

Implement caching

Would it make sense to have a built-in template cache which is enabled in production only (non debug)?. I can see you're currently doing this in https://github.com/macaron-contrib/pongo2/blob/master/pongo2.go where you're integrating pongo2 with a web framework. I think it would make sense to have the template cache in pongo2. Otherwise this work has to be redone when integrating with multiple frameworks which is a hassle.

Could be as simple as if the template is in cache and we're not in debug, load it from cache, otherwise reparse it.

Error message missing file path

I have a lot of go-style template file,

However, the error information provided by missing pongo prompted for a file path.

When I have a lot of template files,

I find the error is very difficult,

Expect you to provide a modified version with a file path information as soon as possible.

This would be a great help.
Thank you.

Reusable macros

in jinja2 you can do something like this:

{% from "helpers/macros.html" import my_reusable_macro, my_reusable_macro2 %}

{{ my_reusable_macro() }}

Would it make sense to implement so macros can be imported and reused instead of them having to be in the same template file as they're used in?

Error message missing file path(#2)

panic: open templates\header: The system cannot find the file specified.

Now I found another mistake information missing file path problem.
I know this error is due to the lack of HTML suffix caused.
But, I don't know who is calling this a line of code, I need to know who is calling.

Allow function calls in {% if %} without returning *Value

If I do something like {% if page.PublishTime.IsZero %}, I get a panic "function return type of 'page.PublishTime.IsZero' must be of type *Value."

It would be very convenient if at least regular functions that return a boolean could be used in if blocks. This doesn't just apply to time.Time, but also any other struct type passed into templates.

What do you think?

The truncatewords tag does not support Chinese

case 1:
{{ "中文字符"|truncatewords:1|safe }}
The expected output a character (中), but in fact, if there is no space, then is unable to determine, so lead to dividing Chinese characters. Because the Chinese character do not need to rely on a space character segmentation.

case 2:
{{ "中文 字符"|truncatewords:1|safe }}
Expected output a character (中), but in fact the output two characters(中文).

Data sql join query can not be displayed in the pongo2

sample database:

type Topic struct {
    Id                int64
    Uid               int64
    Pid               int64
        Title             string
    Content       string
        Created       time.Time
        ...
}

type User struct {
    Id             int64
    Content        string
        Created       time.Time
        ...
}

Real sql statement is executed:

SELECT * FROM `topic` LEFT JOIN user ON user.id = topic.uid WHERE topic.pid=1 or topic.id=1 ORDER BY topic.id DESC, topic.views DESC, topic.reply_count DESC, topic.created DESC LIMIT 25

pongo2 template:

{% if data.topics %}
                        {% for topic in data.topics %}
                            <a href="/topic/{{ topic.Id }}/">{{ topic.Title }}The title can be displayed as two tables only one table has a title field</a>
                        {% endfor %}
{% endif %}

For example,The title can be displayed as two tables only one table has a title field.
However topic.Id not be displayed, because the two tables contain the id field.

My question is, how should I display topic.Id similar situations in the template?

Update Error object with location data when moving the stack up

When returning an Error object, it might have no location data (for example if it's returned by a filter). While moving the Error object the stack up it should be filled with location data if it's missing (at a place where the location data is available).

Nested {% block %}

I tried to set up my templates like this:

- skeleton.html (has basic HTML structure and {% block body %} under <body>)
    - base.html ({% extends "skeleton.html" %} and fills "body" with a header, footer etc, as well as {% block content %})
        - page.html (extends base.html and fills {% block content %}...{% endblock %})

This is valid in Django, and something I often do, but not currently possible with Pongo2:
The 'block' tag can only defined on root level (especially no nesting).

Pointer to Context not needed?

I was playing around with creating a small helper to use Pongo2 with Beego, and came up with this: https://github.com/oal/beego-pongo2

While trying to find a clean way to allow users of beego-pongo2 to use pongo2 without importing it to their own code, I got some input from #go-nuts. @dominikh pointed out that there is no reason to need a pointer to Context in template.Execute(RW) as it is just a map.

https://github.com/flosch/pongo2/blob/master/template.go#L59

Is there a reason behind this? If not, maybe it's better to change the API now that it's still in beta.

Thanks for all your work on pongo(2)!

  • Olav.

Constants

Hello
How do you think, do pongo2 needs to have constants?
For me it looks like in templates

{# or expressions can be not allowed #}
{% const NAME = "any expression" %}

and in go code

const NAME = ""
context := pongo2.Context{
    "NAME": pongo2.AsConst(NAME), // or any expression
}

And of course nothing can't change it.

Wrong associativity for infix operators.

The expression {{34/3*3}} should evaluate as (34/3)_3 ideally (assuming equal precedence for * and /, and left-associativity for /, as in C), but instead evaluates as 34/(3_3) and results in the value of 3 (=34/9) rather than 33 (=11*3).

Base directory

I usually have my templates in a separate directory e.g. structures something like:

templates/base.html
templates/user/index.html

In user.html I'd like to do something like:

{% extends "base.html" %}

Currently having to reference it like {% extends "../base.html" %}. I'd prefer if I could specify a template root and then reference it like a sandbox. It would also let me render "user/index" within the sandbox instead of specifying the full path. Does this make sense? Thanks for a great project, it's looking very useful.

Error page

Is there any way of baking in a simple error page which shows where the error is, what line and the parsed template next to it with line numbers and highlight where the error is. Wondering how hard it would be to implement.

Potential bug in the reflection system

Not sure how to word this without giving an example. I got a struct I'm trying to call from a template:

type Flash struct {
    Category string
    Message  string
}

func (this *Flash) BootstrapClass() string {
    ret := "notice"
    if this.Category == "error" {
        ret = "alert"
    }

    return ret
}

Now in the template if I do:

{{ flash.BootstrapClass() }} 

The BootstrapClass() function doesn't get called, nothing happens. However if I change the BootstrapClass() signature's receiver to a non-pointer it works.

func (this Flash) BootstrapClass() string {
    ret := "notice"
    if this.Category == "error" {
        ret = "alert"
    }

    return ret
}

Now it works fine. Shouldn't the reflection system be smart enough to implicitly convert it? In my opinion both signatures should work.

Struct's method calling not working

When we have to print the result of a strutc's method it doesn't show anything.

Ex.:

type Test struct {
    Child string
}

func (t *Test) MyFunc() string {
    return "My Func Result"
}

func main() {
    tpl, err := pongo2.FromString("Child: {{ Test.Child }} | MyFunc: {{ Test.MyFunc() }}")
    if err != nil {
        panic(err)
    }
    out, err := tpl.Execute(pongo2.Context{"Test":  &Test{ Child: "Test Child"} })
    if err != nil {
        panic(err)
    }
    fmt.Println(out) // Child: TestChild | MyFunc: 
}

What I could figure out is that when parsing Pongo2 knows that a function should be called, but when resolving, the function is not called and only the struct's properties is printed.

Unregister and/or Replace tag/filter

Hello!
It would be great to be able to unregister or replace any of basic tag and filter. It's can be useful when I want change behavior of any tag without creating pull request to your master repository.
Thanks

Cache template in memory

As far as I understand the source code, tags like extends and block only been handled in Execute time, which makes every request to load parent template html from file system. Do you have plan to have a cache in memory option, or I didn't understand right?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.