flask模板

轉自:http://spacewander.github.io/explore-flask-zh/8-templates.html

模板

儘管Flask並不強迫你使用某個特定的模板語言,它仍是默認你會使用Jinja。在Flask社區的大多數開發者使用Jinja,而且我建議你也跟着作。有一些插件容許你用其餘模板語言進行替代(好比Flask-GenshiFlask-Mako),但除非你有充分理由(不懂Jinja可不是一個充分的理由!),不然請保持那個默認的選項;這樣你會避免浪費不少時間來焦頭爛額。html

注意 幾乎全部說起Jinja的資源講的都是Jinja2。Jinja1確實曾存在過,但在這裏咱們不會講到它。當你看到Jinja時,咱們討論的是這個Jinja: http://jinja.pocoo.org/python

Jinja快速入門

Jinja文檔在解釋這門語言的語法和特性這方面作得很棒。在這裏我不會囉嗦一遍,但仍是會再一次向你強調下面一點:git

Jinja有兩種定界符。{% ... %}{{ ... }}。前者用於執行像for循環或賦值等語句,後者向模板輸出一個表達式的結果。github

參見: http://jinja.pocoo.org/docs/templates/#synopsisflask

怎樣組織模板

因此要將模板放進咱們的應用的哪裏呢?若是你是從頭開始閱讀的本文,你可能注意到了Flask在對待你如何組織項目結構的事情上十分隨意。模板也不例外。你大概也已經注意到,總會有一個放置文件的推薦位置。記住兩點。對於模板,這個最佳位置是放在包文件夾下。api

myapp/
    __init__.py
    models.py
    views/
    templates/
    static/
run.py
requirements.txt

讓咱們打開模板文件夾看看。session

templates/
    layout.html
    index.html
    about.html
    profile/
        layout.html
        index.html
    photos.html
    admin/
        layout.html
        index.html
        analytics.html

模板的結構平行於對應的路由的結構。對應於路由myapp.com/admin/analytics的模板是templates/admin/analytics.html。這裏也有一些額外的模板不會被直接渲染。layout.html文件就是用於被其餘模板繼承的。app

繼承

就像蝙蝠俠同樣,一個組織良好的模板文件夾也離不開繼承帶來的好處。基礎模板一般定義了一個適用於全部的子模板的主體結構。在咱們的例子裏,layout.html是一個基礎模板,而其餘的html文件都是子模板。模塊化

一般,你會有一個頂級的layout.html定義你的應用的主體佈局,外加站點的每個節點也有本身的一個layout.html。若是再看一眼上面的文件夾結構,你會看到一個頂級的myapp/templates/layout.html,以及myapp/templates/profile/layout.htmlmyapp/templates/admin/layout.html。後兩個文件繼承並修改第一個文件。函數

繼承是經過{% extends %}{% block %}標籤實現的。在雙親模板中,你能夠定義要給子模板處理的block。

myapp/templates/layout.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>{% block title %}{% endblock %}</title>
    </head>
    <body>
    {% block body %}
        <h1>這個標題在雙親模板中定義</h1>
    {% endblock %}
    </body>
</html>

在子模板中,你能夠拓展雙親模板並定義block裏面的內容。

myapp/templates/index.html

{% extends "layout.html" %}
{% block title %}Hello world!{% endblock %}
{% block body %}
    {{ super() }}
    <h2>這個標題在子模板中定義</h2>
{% endblock %}

super()函數讓咱們在子模板里加載雙親模板中這個block的內容。

參見 若想了解更多關於繼承的內容,請移步到Jinja模板繼承方面的文檔。http://jinja.pocoo.org/docs/templates/#template-inheritance

建立宏

憑藉將反覆出現的代碼片斷抽象成,咱們能夠實現DRY原則(Don't Repeat Yourself)。在撰寫用於應用的導航功能的HTML時,咱們可能會須要給「活躍」連接(好比,到當前頁面的連接)一個不一樣的類。若是沒有宏,咱們將不得不使用一大堆if/else語句來從每一個連接中過濾出「活躍」連接。

宏提供了模塊化模板代碼的一種方式;它們就像是函數同樣。讓咱們看一下如何使用宏來標記活躍連接。

myapp/templates/layout.html

{% from "macros.html" import nav_link with context %}
<!DOCTYPE html>
<html lang="en">
    <head>
    {% block head %}
        <title>個人應用</title>
    {% endblock %}
    </head>
    <body>
        <ul class="nav-list">
            {{ nav_link('home', 'Home') }}
            {{ nav_link('about', 'About') }}
            {{ nav_link('contact', 'Get in touch') }}
        </ul>
    {% block body %}
    {% endblock %}
    </body>
</html>

如今咱們調用了一個還沒有定義的宏 - nav_link - 並傳遞兩個參數給它:一個目標(好比目標視圖的函數名)和咱們想要展現的文本。

注意 你可能注意到了咱們在import語句中加入了with context。Jinja的上下文(context)包括了經過render_template()函數傳遞的參數以及在咱們的Python代碼的Jinja環境上下文。這些變量可以被用於模板的渲染。

一些變量是咱們顯式傳遞過去的,好比render_template("index.html", color="red"),但還有些變量和函數是Flask自動加入到上下文的,好比requestgsession。使用了{% from ... import ... with context %},咱們告訴Jinja讓全部的變量也在宏裏可用。

參見

是時候定義模板中用的nav_link宏了。

myapp/templates/macros.html

{% macro nav_link(endpoint, text) %}
{% if request.endpoint.endswith(endpoint) %}
    <li class="active"><a href="{{ url_for(endpoint) }}">{{text}}</a></li>
{% else %}
    <li><a href="{{ url_for(endpoint) }}">{{text}}</a></li>
{% endif %}
{% endmacro %}

如今咱們已經在myapp/templates/macros.html中定義了一個宏。咱們所作的,就是使用Flask的request對象 - 默認在Jinja上下文中可用 - 來檢查當前路由是不是傳遞給nav_link的那個路由參數。若是是,咱們就在目標連接指向的頁面上,因而能夠標記它爲活躍的。

注意 from x import y語句中要求x是相對於y的相對路徑。若是咱們的模板位於myapp/templates/user/blog.html,咱們須要使用from "../macros.html" import nav_link with context

自定義過濾器

Jinja過濾器是在渲染成模板以前,做用於{{ ... }}中的表達式的值的函數。

<h2>{{ article.title|title }}</h2>

在這個代碼中,title過濾器接受article.title並返回一個標題格式的文本,用於輸出到模板中。它的語法,以及功能,皆一如Unix中修改程序輸出的「管道」同樣。

參見 除了title,還有許許多多別的內建的過濾器。在這裏能夠看到完整的列表:http://jinja.pocoo.org/docs/templates/#builtin-filters

咱們能夠自定義用於Jinja模板的過濾器。做爲例子,咱們將實現一個簡單的caps過濾器來使字符串中全部的字母大寫。

注意 Jinja已經有一個upper過濾器能實現這一點,還有一個capitalize過濾器能大寫第一個字符並小寫剩餘字符。這些過濾器還能處理Unicode轉換,不過咱們的這個例子將只專一於闡述相關概念。

咱們將在myapp/util/filters.py中定義咱們的過濾器。這個util包能夠用來放置各類雜項。

myapp/util/filters.py

from .. import app

@app.template_filter()
def caps(text):
    """Convert a string to all caps."""
    return text.uppercase()

在上面的代碼中,經過@app.template_filter()裝飾器,咱們能將某個函數註冊成Jinja過濾器。默認的過濾器名字就是函數的名字,可是經過傳遞一個參數給裝飾器,你能夠改變它:

@app.template_filter('make_caps')
def caps(text):
    """Convert a string to all caps."""
    return text.uppercase()

如今咱們能夠在模板中調用make_caps而不是caps{{ "hello world!"|make_caps }}

爲了讓咱們的過濾器在模板中可用,咱們僅須要在頂級init.py中import它。

myapp/init.py

# 確保app已經被初始化以避免致使循環import
from .util import filters

總結

  • 使用Jinja做爲模板語言。
  • Jinja有兩種定界符:{% ... %}{{ ... }}。前者用於執行相似循環或賦值的語句,後者向模板輸出表達式求值的結果。
  • 模板應該放在myapp/templates/ - 一個在應用文件夾裏面的目錄。
  • 我建議template/文件夾的結構應該與應用URL結構一一對應。
  • 你應該在myapp/templates以及站點的每一部分放置一個layout.html做爲佈局模板。後者是前者的拓展。
  • 能夠用模板語言寫相似於函數的宏。
  • 能夠用Python代碼寫應用在模板中的過濾器函數。
相關文章
相關標籤/搜索