创建模板
Askama 模板通过定义 struct 来提供模板上下文,并关联一个 UTF-8 编码的文本文件(或内联源码,见下文)。Askama 可用于生成任何基于文本的格式。模板文件的扩展名可用于提供内容类型提示。
模板由文本内容(原样传递)、表达式(渲染时会被替换为内容)以及标签(控制模板逻辑)组成。模板语法 使用非常简单的 Jinja语法,并与Jinja 衍生品Twig or Tera非常相似.
#![allow(unused)]
fn main() {
#[derive(Template)] // 驱动生成模板代码...
#[template(path = "hello.html")] // 使用该路径下的模板,路径相对于 crate 根目录下的 `templates` 目录
struct HelloTemplate<'a> { // 结构体名称可以是任意合法标识符
name: &'a str, // 字段名应与模板中的变量名匹配
}
}
template() 属性
Askama 通过为所有带有 #[derive(Template)] 属性的结构体类型生成一个或多个 trait 实现模板功能。代码生成过程接受一些选项,可通过 template() 属性指定。目前支持以下子属性:
(例如 path = “foo.html”):设置模板文件的路径。该路径相对于配置的模板目录(默认情况下,是 Cargo.toml 旁边的 templates 目录)进行解析。文件扩展名用于推断转义模式(见下文)。在 Web 框架集成中,路径的扩展名也可用于推断生成响应的内容类型。不能与 source 同时使用。
#![allow(unused)]
fn main() {
#[derive(Template)]
#[template(path = "hello.html")]
struct HelloTemplate<'a> { ... }
}
(例如 source = “{{ foo }}”):直接设置模板源码。这对于测试用例或短模板很有用。生成的路径未定义,通常使得无法从其他模板引用此模板。如果指定了 source,则还必须指定 ext(见下文)。不能与 path 同时使用。
#![allow(unused)]
fn main() {
#[derive(Template)]
#[template(source = "Hello {{ name }}")]
struct HelloTemplate<'a> {
name: &'a str,
}
}
-
in_doc(例如
in_doc = true):请参阅 “documentation as template code”。 -
ext(例如
ext = "txt"):允许将内容类型指定为文件扩展名。这用于推断转义模式(见下文),某些 Web 框架集成会用它来确定内容类型。不能与 path 同时使用。#![allow(unused)] fn main() { #[derive(Template)] #[template(source = "Hello {{ name }}", ext = "txt")] struct HelloTemplate<'a> { name: &'a str, } } -
print(例如
print = "code"):启用调试,打印内容可以是空(none)、解析后的语法树(ast)、生成的代码(code)或两者都打印(all)。所请求的数据将在编译时打印到标准输出。#![allow(unused)] fn main() { #[derive(Template)] #[template(path = "hello.html", print = "all")] struct HelloTemplate<'a> { ... } } -
block(例如
block = "block_name"):单独渲染块。块外部的表达式不会被结构体要求,并且同样支持继承。当你需要分解模板进行部分渲染,而又不想将局部内容提取到单独的模板或宏中时,此选项很有用。#![allow(unused)] fn main() { #[derive(Template)] #[template(path = "hello.html", block = "hello")] struct HelloTemplate<'a> { ... } } -
blocks(例如
blocks = ["title", "content"]):自动生成(若干)子模板,其行为就像带有 block = “…” 属性一样。你可以通过方法my_template.as_block_name()访问这些子模板,其中 block_name 是块的名称:#![allow(unused)] fn main() { #[derive(Template)] #[template( ext = "txt", source = " {% block title %} ... {% endblock %} {% block content %} ... {% endblock %} ", blocks = ["title", "content"] )] struct News<'a> { title: &'a str, message: &'a str, } let news = News { title: "Announcing Rust 1.84.1", message: "The Rust team has published a new point release of Rust, 1.84.1.", }; assert_eq!( news.as_title().render().unwrap(), "<h1>Announcing Rust 1.84.1</h1>" ); } -
escape(例如
escape = "none"):覆盖模板扩展名,用于确定此模板的转义器。有关配置自定义转义器的更多信息,请参见相关章节。#![allow(unused)] fn main() { #[derive(Template)] #[template(path = "hello.html", escape = "none")] struct HelloTemplate<'a> { ... } } -
syntax(例如
syntax = "foo"):设置配置文件中定义的解析器的语法名称。默认语法是 “default”,即 Askama 提供的语法。#![allow(unused)] fn main() { #[derive(Template)] #[template(path = "hello.html", syntax = "foo")] struct HelloTemplate<'a> { ... } } -
config(例如
config = "config_file_path"):设置要使用的配置文件的路径。该路径相对于你的 crate 根目录进行解析。#![allow(unused)] fn main() { #[derive(Template)] #[template(path = "hello.html", config = "config.toml")] struct HelloTemplate<'a> { ... } } -
askama(例如
askama = askama): 如果你在子项目、库或macro中使用 askama,可能需要指定查找 askama 模块的path:#![allow(unused)] fn main() { #[doc(hidden)] use askama as __askama; #[macro_export] macro_rules! new_greeter { ($name:ident) => { #[derive(Debug, $crate::askama::Template)] #[template( ext = "txt", source = "Hello, world!", askama = $crate::__askama )] struct $name; } } new_greeter!(HelloWorld); assert_eq!(HelloWorld.to_string(), Ok("Hello, world.")); }
枚举实现模板
可以为 struct 和 enum 添加 Template derive。如果仅对条目本身添加 #[template()],则两种条目类型的工作方式完全相同。但对于 enum,你还可以选择为其中一个、部分或所有变体添加专门的实现:
#![allow(unused)]
fn main() {
#[derive(Debug, Template)]
#[template(path = "area.txt")]
enum Area {
Square(f32),
Rectangle { a: f32, b: f32 },
Circle { radius: f32 },
}
}
{%- match self -%}
{%- when Self::Square(side) -%}
{{side}}^2
{%- when Self::Rectangle { a, b} -%}
{{a}} * {{b}}
{%- when Self::Circle { radius } -%}
pi * {{radius}}^2
{%- endmatch -%}
将得到与以下代码相同的结果:
#![allow(unused)]
fn main() {
#[derive(Template, Debug)]
#[template(ext = "txt")]
enum AreaPerVariant {
#[template(source = "{{self.0}}^2")]
Square(f32),
#[template(source = "{{a}} * {{b}}")]
Rectangle { a: f32, b: f32 },
#[template(source = "pi * {{radius}}^2")]
Circle { radius: f32 },
}
}
如你所见,通过 ext 属性,enum 变体会继承 enum 的大部分设置:config、escape、ext、syntax 和 whitespace。不继承的有:block 和 print。
如果某个 enum 变体没有 #[template] 注解,那么该 enum 需要一个默认实现,当 self 为该变体时会使用它。仅在模板上注解或在所有变体上注解之间的一种良好折衷,可能是对成员使用 block 参数:
#![allow(unused)]
fn main() {
#[derive(Template, Debug)]
#[template(path = "area.txt")]
enum AreaWithBlocks {
#[template(block = "square")]
Square(f32),
#[template(block = "rectangle")]
Rectangle { a: f32, b: f32 },
#[template(block = "circle")]
Circle { radius: f32 },
}
}
{%- block square -%}
{{self.0}}^2
{%- endblock -%}
{%- block rectangle -%}
{{a}} * {{b}}
{%- endblock -%}
{%- block circle -%}
pi * {{radius}}^2
{%- endblock -%}
将文档作为模板代码
作为将模板代码放在外部文件(例如 path argument)或字符串(例如 source argument)中的替代方案,你也可以启用 "code-in-doc" 特性。启用后,你可以直接在模板条目的文档中指定模板代码。
此时,不在 #[template] 属性中使用 path = "…" 或 source = "…",而是指定 in_doc = true,并在条目的文档中添加一个带有 askama 属性的代码块:
#![allow(unused)]
fn main() {
/// Here you can put our usual comments.
///
/// ```askama
/// <div>{{ lines|linebreaksbr }}</div>
/// ```
///
/// Any usual docs, including tests can be put in here, too:
///
/// ```rust
/// assert_eq!(
/// Example { lines: "a\nb\nc" }.to_string(),
/// "<div>a<br/>b<br/>c</div>"
/// );
/// ```
///
/// All comments are still optional, though.
#[derive(Template)]
#[template(ext = "html", in_doc = true)]
struct Example<'a> {
lines: &'a str,
}
}
如果你想在注释中提供模板代码,则还必须指定 ext 参数,例如 #[template(ext = "html")]。
除了 askama,你还可以写 jinja 或 jinja2,例如以便更好地与语法高亮工具配合使用。