Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

创建模板

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."));
    }

枚举实现模板

可以为 structenum 添加 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 的大部分设置:configescapeextsyntaxwhitespace。不继承的有:blockprint

如果某个 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,你还可以写 jinjajinja2,例如以便更好地与语法高亮工具配合使用。