mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-30 14:31:36 +00:00
Prune duplicate documentation
This commit is contained in:
parent
435bb0569c
commit
9f96214cbe
60
README.md
60
README.md
@ -8,7 +8,7 @@
|
||||
Askama implements a template rendering engine based on Jinja.
|
||||
It generates Rust code from your templates at compile time
|
||||
based on a user-defined `struct` to hold the template's context.
|
||||
See below for an example, or read [the documentation][docs].
|
||||
See below for an example, or read [the book][docs].
|
||||
|
||||
**"I use Askama for actix's TechEmpower benchmarks."** --
|
||||
[Nikolay Kim][fafhrd91], creator of actix-web
|
||||
@ -46,11 +46,7 @@ in a for-profit context, please consider supporting my open source work on
|
||||
* Opt-out HTML escaping
|
||||
* Syntax customization
|
||||
|
||||
### Limitations
|
||||
|
||||
* A limited number of built-in filters have been implemented
|
||||
|
||||
[docs]: https://docs.rs/askama
|
||||
[docs]: https://djc.github.io/askama/
|
||||
[fafhrd91]: https://github.com/fafhrd91
|
||||
[mitsuhiko]: http://lucumr.pocoo.org/
|
||||
[issues]: https://github.com/djc/askama/issues
|
||||
@ -102,55 +98,3 @@ You should now be able to compile and run this code.
|
||||
Review the [test cases] for more examples.
|
||||
|
||||
[test cases]: https://github.com/djc/askama/tree/master/testing
|
||||
|
||||
|
||||
Debugging and troubleshooting
|
||||
-----------------------------
|
||||
|
||||
You can view the parse tree for a template as well as the generated code by
|
||||
changing the `template` attribute item list for the template struct:
|
||||
|
||||
```rust
|
||||
#[derive(Template)]
|
||||
#[template(path = "hello.html", print = "all")]
|
||||
struct HelloTemplate<'a> { ... }
|
||||
```
|
||||
|
||||
The `print` key can take one of four values:
|
||||
|
||||
* `none` (the default value)
|
||||
* `ast` (print the parse tree)
|
||||
* `code` (print the generated code)
|
||||
* `all` (print both parse tree and code)
|
||||
|
||||
The resulting output will be printed to `stderr` during the compilation process.
|
||||
|
||||
The parse tree looks like this for the example template:
|
||||
|
||||
```
|
||||
[Lit("", "Hello,", " "), Expr(WS(false, false), Var("name")),
|
||||
Lit("", "!", "\n")]
|
||||
```
|
||||
|
||||
The generated code looks like this:
|
||||
|
||||
```rust
|
||||
impl < 'a > ::askama::Template for HelloTemplate< 'a > {
|
||||
fn render_into(&self, writer: &mut ::std::fmt::Write) -> ::askama::Result<()> {
|
||||
write!(
|
||||
writer,
|
||||
"Hello, {expr0}!",
|
||||
expr0 = &::askama::MarkupDisplay::from(&self.name),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
fn extension() -> Option<&'static str> {
|
||||
Some("html")
|
||||
}
|
||||
}
|
||||
impl < 'a > ::std::fmt::Display for HelloTemplate< 'a > {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
::askama::Template::render_into(self, f).map_err(|_| ::std::fmt::Error {})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -7,6 +7,9 @@
|
||||
//! For feature highlights and a quick start, please review the
|
||||
//! [README](https://github.com/djc/askama/blob/master/README.md).
|
||||
//!
|
||||
//! The primary documentation for this crate now lives in
|
||||
//! [the book](https://djc.github.io/askama/).
|
||||
//!
|
||||
//! # Creating Askama templates
|
||||
//!
|
||||
//! An Askama template is a `struct` definition which provides the template
|
||||
@ -55,439 +58,6 @@
|
||||
//! * `syntax` (as `syntax = "foo"`): set the syntax name for a parser defined
|
||||
//! in the configuration file. The default syntax , "default", is the one
|
||||
//! provided by Askama.
|
||||
//!
|
||||
//! ## Configuration
|
||||
//!
|
||||
//! At compile time, Askama will read optional configuration values from
|
||||
//! `askama.toml` in the crate root (the directory where `Cargo.toml` can
|
||||
//! be found). Currently, this covers the directories to search for templates,
|
||||
//! custom syntax configuration and escaper configuration.
|
||||
//!
|
||||
//! This example file demonstrates the default configuration:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [general]
|
||||
//! # Directories to search for templates, relative to the crate root.
|
||||
//! dirs = ["templates"]
|
||||
//! ```
|
||||
//!
|
||||
//! Here is an example that defines two custom syntaxes:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [general]
|
||||
//! default_syntax = "foo"
|
||||
//!
|
||||
//! [[syntax]]
|
||||
//! name = "foo"
|
||||
//! block_start = "%{"
|
||||
//! comment_start = "#{"
|
||||
//! expr_end = "^^"
|
||||
//!
|
||||
//! [[syntax]]
|
||||
//! name = "bar"
|
||||
//! block_start = "%%"
|
||||
//! block_end = "%%"
|
||||
//! comment_start = "%#"
|
||||
//! expr_start = "%{"
|
||||
//! ```
|
||||
//!
|
||||
//! A syntax block consists of at least the attribute `name` which uniquely
|
||||
//! names this syntax in the project.
|
||||
//!
|
||||
//! The following keys can currently be used to customize template syntax:
|
||||
//!
|
||||
//! * `block_start`, defaults to `{%`
|
||||
//! * `block_end`, defaults to `%}`
|
||||
//! * `comment_start`, defaults to `{#`
|
||||
//! * `comment_end`, defaults to `#}`
|
||||
//! * `expr_start`, defaults to `{{`
|
||||
//! * `expr_end`, defaults to `}}`
|
||||
//!
|
||||
//! Values must be 2 characters long and start delimiters must all start with the same
|
||||
//! character. If a key is omitted, the value from the default syntax is used.
|
||||
//!
|
||||
//! Here is an example of a custom escaper:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [[escaper]]
|
||||
//! path = "::tex_escape::Tex"
|
||||
//! extensions = ["tex"]
|
||||
//! ```
|
||||
//!
|
||||
//! An escaper block consists of the attributes `path` and `name`. `path`
|
||||
//! contains a Rust identifier that must be in scope for templates using this
|
||||
//! escaper. `extensions` defines a list of file extensions that will trigger
|
||||
//! the use of that escaper. Extensions are matched in order, starting with the
|
||||
//! first escaper configured and ending with the default escapers for HTML
|
||||
//! (extensions `html`, `htm`, `xml`, `j2`, `jinja`, `jinja2`) and plain text
|
||||
//! (no escaping; `md`, `yml`, `none`, `txt`, and the empty string). Note that
|
||||
//! this means you can also define other escapers that match different extensions
|
||||
//! to the same escaper.
|
||||
//!
|
||||
//! ## Variables
|
||||
//!
|
||||
//! Top-level template variables are defined by the template's context type.
|
||||
//! You can use a dot (`.`) to access variable's attributes or methods.
|
||||
//! Reading from variables is subject to the usual borrowing policies.
|
||||
//! For example, `{{ name }}` will get the ``name`` field from the template
|
||||
//! context,
|
||||
//! while `{{ user.name }}` will get the ``name`` field of the ``user``
|
||||
//! field from the template context.
|
||||
//!
|
||||
//! ## Assignments
|
||||
//!
|
||||
//! Inside code blocks, you can also declare variables or assign values
|
||||
//! to variables.
|
||||
//! Assignments can't be imported by other templates.
|
||||
//!
|
||||
//! Assignments use the let tag:
|
||||
//!
|
||||
//! ```text
|
||||
//! {% let name = user.name %}
|
||||
//! {% let len = name.len() %}
|
||||
//!
|
||||
//! {% let val -%}
|
||||
//! {% if len == 0 -%}
|
||||
//! {% let val = "foo" -%}
|
||||
//! {% else -%}
|
||||
//! {% let val = name -%}
|
||||
//! {% endif -%}
|
||||
//! {{ val }}
|
||||
//! ```
|
||||
//!
|
||||
//! ## Filters
|
||||
//!
|
||||
//! Values such as those obtained from variables can be post-processed
|
||||
//! using **filters**.
|
||||
//! Filters are applied to values using the pipe symbol (`|`) and may
|
||||
//! have optional extra arguments in parentheses.
|
||||
//! Filters can be chained, in which case the output from one filter
|
||||
//! is passed to the next.
|
||||
//!
|
||||
//! For example, `{{ "{:?}"|format(name|escape) }}` will escape HTML
|
||||
//! characters from the value obtained by accessing the `name` field,
|
||||
//! and print the resulting string as a Rust literal.
|
||||
//!
|
||||
//! The built-in filters are documented as part of the
|
||||
//! [filters module documentation](filters/index.html).
|
||||
//!
|
||||
//! To define your own filters, simply have a module named `filters` in
|
||||
//! scope of the context deriving a `Template` `impl`. Note that in case of
|
||||
//! name collision, the built in filters take precedence.
|
||||
//!
|
||||
//! ## Whitespace control
|
||||
//!
|
||||
//! Askama considers all tabs, spaces, newlines and carriage returns to be
|
||||
//! whitespace. By default, it preserves all whitespace in template code,
|
||||
//! except that a single trailing newline character is suppressed.
|
||||
//! However, whitespace before and after expression and block delimiters
|
||||
//! can be suppressed by writing a minus sign directly following a
|
||||
//! start delimiter or leading into an end delimiter.
|
||||
//!
|
||||
//! Here is an example:
|
||||
//!
|
||||
//! ```text
|
||||
//! {% if foo %}
|
||||
//! {{- bar -}}
|
||||
//! {% else if -%}
|
||||
//! nothing
|
||||
//! {%- endif %}
|
||||
//! ```
|
||||
//!
|
||||
//! This discards all whitespace inside the if/else block. If a literal
|
||||
//! (any part of the template not surrounded by `{% %}` or `{{ }}`)
|
||||
//! includes only whitespace, whitespace suppression on either side will
|
||||
//! completely suppress that literal content.
|
||||
//!
|
||||
//! ## Template inheritance
|
||||
//!
|
||||
//! Template inheritance allows you to build a base template with common
|
||||
//! elements that can be shared by all inheriting templates.
|
||||
//! A base template defines **blocks** that child templates can override.
|
||||
//!
|
||||
//! ### Base template
|
||||
//!
|
||||
//! ```text
|
||||
//! <!DOCTYPE html>
|
||||
//! <html lang="en">
|
||||
//! <head>
|
||||
//! <title>{% block title %}{{ title }} - My Site{% endblock %}</title>
|
||||
//! {% block head %}{% endblock %}
|
||||
//! </head>
|
||||
//! <body>
|
||||
//! <div id="content">
|
||||
//! {% block content %}{% endblock %}
|
||||
//! </div>
|
||||
//! </body>
|
||||
//! </html>
|
||||
//! ```
|
||||
//!
|
||||
//! The `block` tags define three blocks that can be filled in by child
|
||||
//! templates. The base template defines a default version of the block.
|
||||
//! A base template must define one or more blocks in order to enable
|
||||
//! inheritance. Blocks can only be specified at the top level of a template
|
||||
//! or inside other blocks, not inside `if`/`else` branches or in `for`-loop
|
||||
//! bodies.
|
||||
//!
|
||||
//! ### Child template
|
||||
//!
|
||||
//! Here's an example child template:
|
||||
//!
|
||||
//! ```text
|
||||
//! {% extends "base.html" %}
|
||||
//!
|
||||
//! {% block title %}Index{% endblock %}
|
||||
//!
|
||||
//! {% block head %}
|
||||
//! <style>
|
||||
//! </style>
|
||||
//! {% endblock %}
|
||||
//!
|
||||
//! {% block content %}
|
||||
//! <h1>Index</h1>
|
||||
//! <p>Hello, world!</p>
|
||||
//! {% endblock %}
|
||||
//! ```
|
||||
//!
|
||||
//! The `extends` tag tells the code generator that this template inherits
|
||||
//! from another template. It will search for the base template relative to
|
||||
//! itself before looking relative to the template base directory. It will
|
||||
//! render the top-level content from the base template, and substitute
|
||||
//! blocks from the base template with those from the child template. Inside
|
||||
//! a block in a child template, the `super()` macro can be called to render
|
||||
//! the parent block's contents.
|
||||
//!
|
||||
//! ## HTML escaping
|
||||
//!
|
||||
//! Askama by default escapes variables if it thinks it is rendering HTML
|
||||
//! content. It infers the escaping context from the extension of template
|
||||
//! filenames, escaping by default if the extension is one of `html`, `htm`,
|
||||
//! or `xml`. When specifying a template as `source` in an attribute, the
|
||||
//! `ext` attribute parameter must be used to specify a type. Additionally,
|
||||
//! you can specify an escape mode explicitly for your template by setting
|
||||
//! the `escape` attribute parameter value (to `none` or `html`).
|
||||
//!
|
||||
//! Askama escapes `<`, `>`, `&`, `"`, `'`, `\` and `/`, according to the
|
||||
//! [OWASP escaping recommendations][owasp]. Use the `safe` filter to
|
||||
//! prevent escaping for a single expression, or the `escape` (or `e`)
|
||||
//! filter to escape a single expression in an unescaped context.
|
||||
//!
|
||||
//! [owasp]: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content
|
||||
//!
|
||||
//! ## Control structures
|
||||
//!
|
||||
//! ### For
|
||||
//!
|
||||
//! Loop over each item in an iterator. For example:
|
||||
//!
|
||||
//! ```text
|
||||
//! <h1>Users</h1>
|
||||
//! <ul>
|
||||
//! {% for user in users %}
|
||||
//! <li>{{ user.name|e }}</li>
|
||||
//! {% endfor %}
|
||||
//! </ul>
|
||||
//! ```
|
||||
//!
|
||||
//! Inside for-loop blocks, some useful variables are accessible:
|
||||
//!
|
||||
//! * *loop.index*: current loop iteration (starting from 1)
|
||||
//! * *loop.index0*: current loop iteration (starting from 0)
|
||||
//! * *loop.first*: whether this is the first iteration of the loop
|
||||
//! * *loop.last*: whether this is the last iteration of the loop
|
||||
//!
|
||||
//! ### If
|
||||
//!
|
||||
//! The *if* statement is used as you might expect:
|
||||
//!
|
||||
//! ```text
|
||||
//! {% if users.len() == 0 %}
|
||||
//! No users
|
||||
//! {% else if users.len() == 1 %}
|
||||
//! 1 user
|
||||
//! {% else %}
|
||||
//! {{ users.len() }} users
|
||||
//! {% endif %}
|
||||
//! ```
|
||||
//!
|
||||
//! ### Match
|
||||
//!
|
||||
//! In order to deal with Rust `enum`s in a type-safe way, templates support
|
||||
//! match blocks from version 0.6. Here is a simple example showing how to
|
||||
//! expand an `Option`:
|
||||
//!
|
||||
//! ```text
|
||||
//! {% match item %}
|
||||
//! {% when Some with ("foo") %}
|
||||
//! Found literal foo
|
||||
//! {% when Some with (val) %}
|
||||
//! Found {{ val }}
|
||||
//! {% when None %}
|
||||
//! {% endmatch %}
|
||||
//! ```
|
||||
//!
|
||||
//! That is, a `match` block can optionally contain some whitespace (but
|
||||
//! no other literal content), followed by a number of `when` blocks
|
||||
//! and an optional `else` block. Each `when` block must name a list of
|
||||
//! matches (`(val)`), optionally introduced with a variant name. The
|
||||
//! `else` block is equivalent to matching on `_` (matching anything).
|
||||
//!
|
||||
//! Struct-like enum variants are supported from version 0.8, with the list
|
||||
//! of matches surrounded by curly braces instead (`{ field }`). New names
|
||||
//! for the fields can be specified after a colon in the list of matches
|
||||
//! (`{ field: val }`).
|
||||
//!
|
||||
//! ### Include
|
||||
//!
|
||||
//! The *include* statement lets you split large or repetitive blocks into
|
||||
//! separate template files. Included templates get full access to the context
|
||||
//! in which they're used, including local variables like those from loops:
|
||||
//!
|
||||
//! ```text
|
||||
//! {% for i in iter %}
|
||||
//! {% include "item.html" %}
|
||||
//! {% endfor %}
|
||||
//! ```
|
||||
//!
|
||||
//! ```text
|
||||
//! * Item: {{ i }}
|
||||
//! ```
|
||||
//!
|
||||
//! The path to include must be a string literal, so that it is known at
|
||||
//! compile time. Askama will try to find the specified template relative
|
||||
//! to the including template's path before falling back to the absolute
|
||||
//! template path. Use `include` within the branches of an `if`/`else`
|
||||
//! block to use includes more dynamically.
|
||||
//!
|
||||
//! ## Expressions
|
||||
//!
|
||||
//! Askama supports string literals (`"foo"`) and integer literals (`1`).
|
||||
//! It supports almost all binary operators that Rust supports,
|
||||
//! including arithmetic, comparison and logic operators.
|
||||
//! The parser applies the same precedence order as the Rust compiler.
|
||||
//! Expressions can be grouped using parentheses.
|
||||
//! The HTML special characters `&`, `<` and `>` will be replaced with their
|
||||
//! character entities unless the `escape` mode is disabled for a template.
|
||||
//! Methods can be called on variables that are in scope, including `self`.
|
||||
//!
|
||||
//! **Warning**: if the result of an expression (a `{{ }}` block) is
|
||||
//! equivalent to `self`, this can result in a stack overflow from infinite
|
||||
//! recursion. This is because the `Display` implementation for that expression
|
||||
//! will in turn evaluate the expression and yield `self` again.
|
||||
//!
|
||||
//! ## Templates in templates
|
||||
//!
|
||||
//! Using expressions, it is possible to delegate rendering part of a template to another template.
|
||||
//! This makes it possible to inject modular template sections into other templates and facilitates
|
||||
//! testing and reuse.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use askama::Template;
|
||||
//! #[derive(Template)]
|
||||
//! #[template(source = "Section 1: {{ s1.render().unwrap() }}", ext = "txt")]
|
||||
//! struct RenderInPlace<'a> {
|
||||
//! s1: SectionOne<'a>
|
||||
//! }
|
||||
//!
|
||||
//! #[derive(Template)]
|
||||
//! #[template(source = "A={{ a }}\nB={{ b }}", ext = "txt")]
|
||||
//! struct SectionOne<'a> {
|
||||
//! a: &'a str,
|
||||
//! b: &'a str,
|
||||
//! }
|
||||
//! let t = RenderInPlace { s1: SectionOne { a: "a", b: "b" } };
|
||||
//! assert_eq!(t.render().unwrap(), "Section 1: A=a\nB=b")
|
||||
//! ```
|
||||
//!
|
||||
//! See the example
|
||||
//! [render in place](https://github.com/djc/askama/blob/master/testing/tests/render_in_place.rs)
|
||||
//! using a vector of templates in a for block.
|
||||
//!
|
||||
//! ## Comments
|
||||
//!
|
||||
//! Askama supports block comments delimited by `{#` and `#}`.
|
||||
//!
|
||||
//!
|
||||
//! ## Recursive Structures
|
||||
//!
|
||||
//! Recursive implementations should preferably use a custom iterator and
|
||||
//! use a plain loop. If that is not doable, call `.render()`
|
||||
//! directly by using an expression as shown below.
|
||||
//! Including self does not work, see #105 and #220 .
|
||||
//!
|
||||
//! ```rust
|
||||
//! use askama::Template;
|
||||
//!
|
||||
//! #[derive(Template)]
|
||||
//! #[template(source = r#"
|
||||
//! //! {% for item in children %}
|
||||
//! {{ item.render().unwrap() }}
|
||||
//! {% endfor %}
|
||||
//! "#, ext = "html", escape = "none")]
|
||||
//! struct Item<'a> {
|
||||
//! name: &'a str,
|
||||
//! children: &'a [Item<'a>],
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Optional functionality
|
||||
//!
|
||||
//! ## Rocket integration
|
||||
//!
|
||||
//! Enabling the `with-rocket` feature appends an implementation of Rocket's
|
||||
//! `Responder` trait for each template type. This makes it easy to trivially
|
||||
//! return a value of that type in a Rocket handler. See
|
||||
//! [the example](https://github.com/djc/askama/blob/master/askama_rocket/tests/basic.rs)
|
||||
//! from the Askama test suite for more on how to integrate.
|
||||
//!
|
||||
//! In case a run-time error occurs during templating, a `500 Internal Server
|
||||
//! Error` `Status` value will be returned, so that this can be further
|
||||
//! handled by your error catcher.
|
||||
//!
|
||||
//! ## Iron integration
|
||||
//!
|
||||
//! Enabling the `with-iron` feature appends an implementation of Iron's
|
||||
//! `Modifier<Response>` trait for each template type. This makes it easy to
|
||||
//! trivially return a value of that type in an Iron handler. See
|
||||
//! [the example](https://github.com/djc/askama/blob/master/askama_iron/tests/basic.rs)
|
||||
//! from the Askama test suite for more on how to integrate.
|
||||
//!
|
||||
//! Note that Askama's generated `Modifier<Response>` implementation currently
|
||||
//! unwraps any run-time errors from the template. If you have a better
|
||||
//! suggestion, please [file an issue](https://github.com/djc/askama/issues/new).
|
||||
//!
|
||||
//! ## Actix-web integration
|
||||
//!
|
||||
//! Enabling the `with-actix-web` feature appends an implementation of Actix-web's
|
||||
//! `Responder` trait for each template type. This makes it easy to trivially return
|
||||
//! a value of that type in an Actix-web handler. See
|
||||
//! [the example](https://github.com/djc/askama/blob/master/askama_actix/tests/basic.rs)
|
||||
//! from the Askama test suite for more on how to integrate.
|
||||
//!
|
||||
//! ## Gotham integration
|
||||
//!
|
||||
//! Enabling the `with-gotham` feature appends an implementation of Gotham's
|
||||
//! `IntoResponse` trait for each template type. This makes it easy to trivially
|
||||
//! return a value of that type in a Gotham handler. See
|
||||
//! [the example](https://github.com/djc/askama/blob/master/askama_gotham/tests/basic.rs)
|
||||
//! from the Askama test suite for more on how to integrate.
|
||||
//!
|
||||
//! In case of a run-time error occurring during templating, the response will be of the same
|
||||
//! signature, with a status code of `500 Internal Server Error`, mime `*/*`, and an empty `Body`.
|
||||
//! This preserves the response chain if any custom error handling needs to occur.
|
||||
//!
|
||||
//! ## Warp integration
|
||||
//!
|
||||
//! Enabling the `with-warp` feature appends an implementation of Warp's `Reply`
|
||||
//! trait for each template type. This makes it simple to return a template from
|
||||
//! a Warp filter. See [the example](https://github.com/djc/askama/blob/master/askama_warp/tests/warp.rs)
|
||||
//! from the Askama test suite for more on how to integrate.
|
||||
//!
|
||||
//! ## The `json` filter
|
||||
//!
|
||||
//! Enabling the `serde-json` filter will enable the use of the `json` filter.
|
||||
//! This will output formatted JSON for any value that implements the required
|
||||
//! `Serialize` trait.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
#[macro_use]
|
||||
|
@ -8,7 +8,7 @@
|
||||
Askama implements a template rendering engine based on Jinja.
|
||||
It generates Rust code from your templates at compile time
|
||||
based on a user-defined `struct` to hold the template's context.
|
||||
See below for an example, or read [the documentation][docs].
|
||||
See below for an example, or read [the book][docs].
|
||||
|
||||
**"I use Askama for actix's TechEmpower benchmarks."** --
|
||||
[Nikolay Kim][fafhrd91], creator of actix-web
|
||||
@ -46,7 +46,7 @@ in a for-profit context, please consider supporting my open source work on
|
||||
* Opt-out HTML escaping
|
||||
* Syntax customization
|
||||
|
||||
[docs]: https://docs.rs/askama
|
||||
[docs]: https://djc.github.io/askama/
|
||||
[fafhrd91]: https://github.com/fafhrd91
|
||||
[mitsuhiko]: http://lucumr.pocoo.org/
|
||||
[issues]: https://github.com/djc/askama/issues
|
||||
|
Loading…
x
Reference in New Issue
Block a user