• 新媒體代運營

    輕松搞定 Django 模板語言進階!

    • 時間:
    • 瀏覽:1187103

     

    作者 | 單雨

    責編 | 屠敏

    模板繼承

    簡介

    模板繼承允許你建立一個基本的"骨架"模板, 它包含了網站中所有常見的元素,并定義了可以被子模板覆蓋的 塊(blocks)  。示例:

    假如父模板base.html如下:

    <!DOCTYPE html>

    <htmllang="en">

    <head>

    <linkrel="stylesheet"href="style.css">

    <title>{% block title %}My amazing site{% endblock %} </title>

    </head>

    <body>

    <divid="sidebar">

    {% block sidebar %}

    <ul>

    <li><ahref="/">Home </a></li>

    <li><ahref="/blog/">Blog </a></li>

    </ul>

    {% endblock %}

    </div>

    <divid="content">

    {% block content %}{% endblock %}

    </div>

    </body>

    </html>

    它定義了一個簡單的 HTML 骨架文檔, 假設這是一個簡單的兩列頁面 。子模板的工作就是填充空的 塊(block) 中的內容  。

    在這個例子中, block 標簽定義了三個可以被子模板填充的塊  。block標簽告訴了模板系統哪些地方可能被子模板覆蓋  。例如 ,子模板可能如下:

    {% extends "base.html" %}

    {% block title %}My amazing blog{% endblock %}

    {% block content %}

    {% for entry in blog_entries %}

    <h2>{{ entry.title }} </h2>

    <p>{{ entry.body }} </p>

    {% endfor %}

    {% endblock %}

    extends 標簽告訴模板系統這個模板繼承了另外的模板  。當模板系統對此模板進行運算時, 首先會尋找他的父模板 ——在這里是"base.html"  。

    在這一點上, 模板引擎會在 base.html 中發現三個 block 標簽, 并且使用子模板的內容替換掉這些塊  。根據變量blog_entries 的值, 輸出可能看起來像這樣:

    <!DOCTYPE html>

    <htmllang="en">

    <head>

    <linkrel="stylesheet"href="style.css">

    <title>My amazing blog </title>

    </head>

    <body>

    <divid="sidebar">

    <ul>

    <li><ahref="/">Home </a></li>

    <li><ahref="/blog/">Blog </a></li>

    </ul>

    </div>

    <divid="content">

    <h2>Entry one </h2>

    <p>This is my first entry. </p>

    <h2>Entry two </h2>

    <p>This is my second entry. </p>

    </div>

    </body>

    </html>

    注意:因為子模板沒有定義 sidebar 塊, 那么父模板的內容就會被使用  。通常來說, 父模板 {% block %} 中的內容會被作為備用的內容  ,在子模板沒有覆蓋時就會被使用  。

    多重繼承

    模板繼承可以是多重繼承 ,多重繼承常見的模式是:

    • 創建一個 base.html 模板把控網站的整體風格 。
    • 為網站的每個子分類創建一個 baseSECTIONNAME.html模板. 比如, basenews.html, base_sports.html 。 這些模板都繼承 base.html 模板 。這些模板中包含特定的設計/風格 。
    • 為每一種類型的頁面創建一個模板, 比如 news article 或blog 內容  。這些模板擴展上一級模板的相應分類  。

    上述的關系可以用下圖表示:

    這樣就能最大限度的重用模板代碼 ,比如在所有頁面通用的導航欄  。

    模板繼承注意事項

    • {% extends %}必須位于模板的最開始  , 如果在其他的部分聲明, 則不生效  。
    • 在基礎模板盡可能多的使用{%block%} ,子模板不需要定義所有父模板中的塊, 所以你可以在若干的塊中填充默認值, 然后定義之后需要自定義的塊  , 有更多的可用塊總是更好的 。
    • 如果發現自己在許多模板中有重復內容的了, 這可能需要移動這些內容到父模板的 {% block %} 中 。
    • 如果需要得到父模板塊中的內容, 可以用{{ block.super }} 變量  。使用 {{ block.super }} 插入的數據不會被自動轉義 ,因為它已經被轉義了  。如果需要轉義, 可以在父模板中轉義  。
    • 使用模板標簽在{% block %}塊外部創建的變量不能在塊內使用 。例如  ,這個模板不渲染任何東西:

    {% trans "Title"astitle %}

    {% block content %}{{ title }}{% endblock %}

    • 為了具有更好的可讀性 ,也可以給{% endblock %}塊標簽定義一個名字  。例如:

    {% block content %}

    ...

    {% endblock content %}

    在一個較長的模板中, 這個方法可以讓你知道是哪一個{% block %} 標簽定義結束了  。

    • 不能在同一模板中定義多個具有相同名稱的塊標簽  。存在這個限制是因為{%block%}標簽是"雙向"定義的 。也就是說, 它不僅指定了子模板要填充父模板的哪個塊, 也說明了父模板要引用哪些子模板塊的內容  。所以在子模板中有多個同名的{%block%}標簽時, 父模板就不知道到底要引用子模板中哪個塊的內容了  。

    自動HTML轉義

    從模板直接生成HTML存在XSS風險

    從模板生成HTML時, 總是有一種風險, 即一個變量將會影響生成的HTML字符 ??紤]這個模板片段:

    Hello, {{ name }}

    看起來可能沒有什么風險  ,但是如果用戶輸入的名字為一個HTML代碼:

    <>alert('hello')</>

    當{{name}}為這個值時  ,模板將呈現為:

    Hello, <>alert('hello')</>

    這將使瀏覽器彈出一個彈出一個Java警告 。同樣的  ,考慮另外一種情況:

    如果名字中包含一個 '<' 符號:

    <b>username

    對應的模板將為:

    Hello, <b>username

    這意味著在此之后的文字將呈現為粗體  。

    由此帶來一個風險:

    用戶提交的數據是不可靠的且不應被直接插入到您的網頁, 因為惡意用戶可以使用這種潛在的漏洞做危害網站的事情  。這種類型的安全漏洞被稱為 Cross Site ing (跨站腳本) (XSS) 攻擊  。

    使用轉義避免XSS風險

    (1)使用escape標簽

    確保讓不受信任的變量經過了 escape 過濾器 , 將危險的HTML字符替換為無害的HTML轉義字符  。但是這常常被忽略 。

    (2)使用自動轉義

    在Django中, 默認每個模板會自動轉義輸出的每一個變量標簽  。具體來說, 這五個字符會被轉義:

    • < 被替換為 <
    • > 被替換為 >
    • ' (單引號) 被替換為 '
    • " (雙引號) 被替換為 "
    • & 被替換為 &

    這種行為是默認的  。如果使用的是Django的模板系統, 自然擁有這種保護  。

    關閉自動轉義

    可以在站點 ,模板和變量三個層級關閉自動轉義  。

    (1)對單個變量

    要為一個單獨的變量禁用自動轉義, 使用 safe 過濾器(https://docs.djangoproject.com/zh-hans/2.2/ref/templates/builtins/#std:templatefilter-safe):

    This will be escaped: {{ data}}

    This will not be escaped: {{ data|safe }}

    如果變量中包含“”字符 ,輸出也是“”  。

    (2)對于模板文本塊

    要在模板中控制自動轉義, 可以在整個模板 (或者模板的特定區域) 使用 autoescape 標簽, 如:

    {% autoescape