1. 程式人生 > >Django 2.1.3 文件-模板-概述

Django 2.1.3 文件-模板-概述

模板

作為一個Web框架,Django需要一種動態生成HTML的便捷方法。最常用的方法依賴於模板。模板包含所需HTML輸出的靜態部分以及描述動態內容將被插入的一些特殊語法。有關建立帶有模板的HTML頁面的示例,請參閱:示例3

Django專案可以配置一個或多個模板引擎(或者不使用模板)。Django後端內建一個自己的模板系統,創造性地稱為Django template language(DTL)和一個流行的替代品

JINJA2。後端也可以使用第三方提供其他可用的模板語言。

Django定義了一個標準的API,用於載入和渲染模板,而不用考慮後端的模板系統。載入包括查詢給定識別符號的模板並對其進行預處理,通常將其編譯的結果儲存在記憶體中。渲染工具將上下文資料插入模板並返回結果字串。

Django 模板語言 是Django自己的模板系統。直到Django 1.8,它是唯一可用的內建選項。這是一個很好的模板庫,即使它是相當僵硬和使用時帶有它自己特質。如果您沒有緊迫的理由需要去選擇另一個後端,則應該使用DTL,特別是如果您正在編寫可插入的應用程式並打算分發模板。在 Django’s contrib apps 中的有些模板,比如

django.contrib.admin ,使用DTL。

由於歷史原因,模板引擎的通用支援和Django模板語言的實現都存在於django.template 模組的名稱空間中。

警告

模板系統使用不可信的模板作者的模板是不安全的。例如,一個站點不應該允許它的使用者提供他們自己的模板,因為模板作者可以做一些事情,比如執行XSS攻擊和拿到包含敏感資訊的模板變數的訪問權。

1. 模板引擎的支援

1.1 配置

模板引擎在TEMPLATES設定中配置。這是一個配置列表,每個引擎一個。預設值為空。在由startproject命令所產生的 settings.py檔案中定義一個有用的值:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # ... some options here ...
        },
    },
]

BACKEND是實現Django模板後端API的模板引擎類的虛擬Python路徑。內建後端是django.template.backends.django.DjangoTemplatesdjango.template.backends.jinja2.Jinja2

由於大多數引擎從檔案載入模板,因此每個引擎的頂級配置包含兩個常用設定:

  • DIRS 定義了模板原始檔的目錄列表,引擎按搜尋順序查詢。
  • APP_DIRS告訴引擎是否應該在已安裝的應用程式中查詢模板。每個後端都定義應用程式中應儲存其模板的子目錄的常規名稱。

雖然不常見,但可以使用不同的選項配置同一後端的多個例項。在這種情況下,您應該為每個引擎定義唯一 的NAME

OPTIONS 包含特定於後端的設定。

1.2 用法

django.template.loader模組定義了兩個用於載入模板的函式。

(1)get_template(template_name,using = None)原始碼
此函式使用給定名稱載入模板並返回一個 Template物件。

返回值的確切型別取決於載入模板的後端。每個後端都有自己的Template類。

get_template()按順序嘗試每個模板引擎,直到成功。如果找不到模板,則會引發模板TemplateDoesNotExist。如果找到模板但包含無效語法,則會引發該模板 TemplateSyntaxError

如何搜尋和載入模板取決於每個引擎的後端和配置。

如果要將搜尋限制為特定模板引擎,請在using引數中傳遞引擎NAME

(2)select_template(template_name_list,using = None)source
select_template()就像是get_template(),除了它採用模板名稱列表。它按順序嘗試每個名稱並返回存在的第一個模板。

如果載入模板失敗,則django.template可能會引發以下兩個異常:

(1)TemplateDoesNotExist(msg,tries = None,backend = None,chain = None)source
無法找到模板時引發此異常。它接受以下可選引數,用於在除錯頁面上填充 模板postmortem

  • backend
    發生異常的模板後端例項。
  • tried
    查詢模板時嘗試的源列表。這被格式化為包含(origin, status)元組的列表,其中origin是origin-like物件,並且status是一個字串,其中包含未找到模板的原因。
  • chain
    嘗試載入模板時引發的TemplateDoesNotExist異常列表。這由get_template()嘗試從多個引擎載入給定模板的函式使用。

(2)TemplateSyntaxError(msg)source
找到模板但包含錯誤時會引發此異常。

(3)Template.render(context = None,request = None)
使用給定的上下文呈現此模板。

通過get_template()和select_template() 返回的Template物件必須提供render()方法。

如果提供context,它必須是字典型別。如果未提供,則引擎將使用空上下文呈現模板。

如果提供request,它必須是HttpRequest。然後引擎必須在模板中提供它以及CSRF令牌。如何實現這一目標取決於每個後端。

這是搜尋演算法的一個例子。對於此示例, TEMPLATES設定為:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            '/home/html/example.com',
            '/home/html/default',
        ],
    },
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [
            '/home/html/jinja2',
        ],
    },
]

如果你呼叫get_template('story_detail.html'),Django將按如下順序查詢的檔案:

  • /home/html/example.com/story_detail.html(‘django’ engine)
    /home/html/default/story_detail.html(‘django’ engine)
    /home/html/jinja2/story_detail.html(‘jinja2’ engine)

如果你呼叫select_template(['story_253_detail.html', 'story_detail.html']),這是Django會尋找的:

  • /home/html/example.com/story_253_detail.html(‘django’ engine)
    /home/html/default/story_253_detail.html(‘django’ engine)
    /home/html/jinja2/story_253_detail.html(‘jinja2’ engine)
    /home/html/example.com/story_detail.html(‘django’ engine)
    /home/html/default/story_detail.html(‘django’ engine)
    /home/html/jinja2/story_detail.html(‘jinja2’ engine)

當Django找到存在的模板時,它會停止查詢。

小貼士
您可以使用select_template()提供靈活的模板載入。例如,如果您撰寫了新聞報道並希望某些故事具有自定義模板,請使用類似select_template(['story_%s_detail.html' % story.id, 'story_detail.html'])的內容 。這將允許您為單個故事使用自定義模板,併為沒有自定義模板的故事提供後備模板。↑

在包含模板的每個目錄中的子目錄中組織模板是可能的 - 也是可取的。慣例是為每個Django應用程式建立一個子目錄,並根據需要在這些子目錄中包含子目錄。

這樣做是為了你自己的理智。將所有模板儲存在單個目錄的根級別會變得混亂。

要載入子目錄中的模板,只需使用斜槓,如下所示:

get_template('news/story_detail.html')

使用與上面TEMPLATES相同的選項,這將嘗試載入以下模板:

  • /home/html/example.com/news/story_detail.html(‘django’ engine)
    /home/html/default/news/story_detail.html(‘django’ engine)
    /home/html/jinja2/news/story_detail.html(‘jinja2’ engine)

此外,為了減少載入和渲染模板的重複性,Django提供了一個自動化過程的快捷功能。

render_to_string(template_name,context = None,request = None,using = None)source
render_to_string() 載入一個模板,如 get_template() ,並立即呼叫它的 render() 方法。它需要下面的引數。

  • template_name
    載入和呈現模板的名稱。如果是模板名稱列表,Django 使用 select_template() ,而不是 get_template() 找到模板。
  • context
    一個字典用作模板的渲染上下文。
  • request
    可選項 HttpRequest 在模板的渲染過程中可用。
  • using
    可選的模板引擎 NAME。對模板的搜尋將限於該引擎。

使用例項:

from django.template.loader import render_to_string
rendered = render_to_string('my_template.html', {'foo': 'bar'})

還可以參看 render() 快捷函式,它呼叫 render_to_string() ,並將結果提供給 HttpResponse ,適合從檢視返回。

最後,您可以直接使用配置好的引擎:

engines
模板引擎可在 django.template.engines 中使用:

from django.template import engines

django_engine = engines['django']
template = django_engine.from_string("Hello {{ name }}!")

在這個例子中,查詢的鍵“django”是引擎的 NAME。


1.3 內建後端

1.3.1 DjangoTemplates(預設後端)

class DjangoTemplates 原始碼
設定BACKEND'django.template.backends.django.DjangoTemplates',以配置Django模板引擎。

如果APP_DIRS是True,DjangoTemplates 引擎在應用程式的templates的子目錄內尋找模板檔案。保留此通用名稱是為了向後相容。

DjangoTemplates引擎接受以下OPTIONS

  • 'autoescape':一個控制是否啟用HTML自動轉義的布林值。
    它預設為True。
    警告 --> 只有在渲染非HTML模板時才設定為False!

  • 'context_processors':用於在使用請求呈現模板時用於填充上下文的可調整的python路徑列表。這些可呼叫物件將請求物件作為其引數,並返回 一個字典而且整合到上下文中。
    預設為空列表。有關RequestContext更多資訊。

  • 'debug':一個開啟/關閉模板除錯模式的布林值。如果是 True,則錯誤頁面將顯示模板呈現期間引發的任何異常的詳細報告。此報告包含模板的相關片段,並突出顯示相應的行。
    它預設為settings.py檔案中DEBUG設定的值。

  • 'loaders':模板載入器類的虛擬Python路徑列表。每個Loader類都知道如何從特定源匯入模板。可選地,可以使用元組而不是字串。元組中的第一項應該是Loader類名,後續項將Loader在初始化期間傳遞給它。
    *預設值取決於值DIRSAPP_DIRS。*有關詳細資訊,請參閱加載器類

  • 'string_if_invalid':作為字串輸出,模板系統應該用於無效(例如拼寫錯誤的)變數。
    *預設為空字串。*有關詳細資訊,請參見如何處理無效變數

  • 'file_charset':用於讀取磁碟上的模板檔案的字符集。
    預設值為FILE_CHARSET

  • 'libraries':用於向模板引擎註冊的模板標記模組的標籤和點綴Python路徑的字典。這可用於新增新庫或為現有庫提供備用標籤。例如:

OPTIONS={
    'libraries': {
        'myapp_tags': 'path.to.myapp.tags',
        'admin.urls': 'django.contrib.admin.templatetags.admin_urls',
    },
}

可以通過將相應的字典的鍵傳遞給{% load %}標籤來載入庫。
{% load static %}

  • 'builtins':要新增到內建函式的模板標記模組的虛擬Python路徑列表。例如:
OPTIONS={
    'builtins': ['myapp.builtins'],
}

無需先呼叫{% load %}標籤即可使用內建庫中的標記和過濾器。

1.3.2 Jinja2

class Jinja2 原始碼
需要安裝Jinja2

 pip install Jinja2

設定BACKEND為 'django.template.backends.jinja2.Jinja2'配置Jinja2引擎。

如果APP_DIRS是True,Jinja2引擎尋找在安裝應用程式的jinja2的子目錄內的模板。

最重要的OPTIONS條目是'environment'。它是返回Jinja2環境的可呼叫的Python路徑。它預設為'jinja2.Environment'。Django呼叫它並將其他選項作為關鍵字引數傳遞。此外,Django添加了與Jinja2不同的預設選項:

  • 'autoescape': True
  • 'loader':為DIRS和 APP_DIRS配置的載入器
  • 'auto_reload': settings.DEBUG
  • 'undefined'DebugUndefined if settings.DEBUG else Undefined

Jinja2引擎也接受以下OPTIONS:

'context_processors':用於在使用請求呈現模板時用於填充上下文的可調整的python路徑列表。這些可呼叫物件將請求物件作為其引數,並返回 一個字典而且整合到上下文中。
預設為空列表。


不鼓勵在Jinja2中使用context_processors。

上下文處理器對Django模板很有用,因為Django模板不支援使用引數呼叫函式。由於Jinja2沒有這個限制,因此建議將使用上下文處理器的函式放在模板可用的全域性變數 jinja2.Environment中,如下所述。然後,您可以在模板中呼叫該函式:
{{ function(request) }}

一些Django模板上下文處理器返回固定值。對於Jinja2模板,這個間接層不是必需的,因為您可以直接新增常量jinja2.Environment

為Jinja2新增context_processors的原始用法包括:

  • 根據請求進行耗時的計算。
  • 需要每個模板的結果。
  • 在每個模板中多次使用結果。
    除非滿足所有這些條件,否則將函式傳遞給模板更簡單,更符合Jinja2的設計。

有目的地將預設配置保持在最低限度。如果一個模板與一個請求呈現(例如,使用時render())時,Jinja2後端在上下問中新增全域性request,csrf_input和 csrf_token。除此之外,這個後端不會建立一個Django風格的環境。它不知道Django過濾器和標籤。要使用特定於Django的API,必須將它們配置到環境中。

例如,您可以建立myproject/jinja2.py,使用以下內容:

from django.templatetags.static import static
from django.urls import reverse

from jinja2 import Environment


def environment(**options):
    env = Environment(**options)
    env.globals.update({
        'static': static,
        'url': reverse,
    })
    return env

並將'environment'選項設定為'myproject.jinja2.environment'

然後你可以在Jinja2模板中使用以下結構:

<img src="{{ static('path/to/company-logo.png') }}" alt="Company Logo">

<a href="{{ url('admin:index') }}">Administration</a>

標籤和過濾器的概念既存在於Django模板語言中,也存在於Jinja2中,但它們的使用方式不同。由於Jinja2支援將引數傳遞給模板中的可呼叫物件,因此可以通過呼叫Jinja2模板中的函式來實現許多需要Django模板中的模板標記或過濾器的功能,如上例所示。Jinja2的全域性名稱空間消除了對模板上下文處理器的需求。Django模板語言沒有等價於Jinja2的測試。

1.3.3 自定義後端

以下是如何實現自定義模板後端以便使用其他模板系統。一個模板後端是一個繼承自django.template.backends.base.BaseEngine的類。它必須實現 get_template()和可選的from_string()。這是一個虛構的foobar模板庫的示例:

from django.template import TemplateDoesNotExist, TemplateSyntaxError
from django.template.backends.base import BaseEngine
from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy

import foobar

class FooBar(BaseEngine):

    # Name of the subdirectory containing the templates for this engine
    # inside an installed application.
    app_dirname = 'foobar'

    def __init__(self, params):
        params = params.copy()
        options = params.pop('OPTIONS').copy()
        super().__init__(params)

        self.engine = foobar.Engine(**options)

    def from_string(self, template_code):
        try:
          return Template(self.engine.from_string(template_code))
        except foobar.TemplateCompilationFailed as exc:
            raise TemplateSyntaxError(exc.args)

    def get_template(self, template_name):
        try:
            return Template(self.engine.get_template(template_name))
        except foobar.TemplateNotFound as exc:
            raise TemplateDoesNotExist(exc.args, backend=self)
        except foobar.TemplateCompilationFailed as exc:
            raise TemplateSyntaxError(exc.args)


class Template:

    def __init__(self, template):
        self.template = template

    def render(self, context=None, request=None):
        if context is None:
            context = {}
        if request is not None:
            context['request'] = request
            context['csrf_input'] = csrf_input_lazy(request)
            context['csrf_token'] = csrf_token_lazy(request)
        return self.template.render(context)

有關更多資訊,請參見DEP 182

1.4 自定義引擎的除錯整合

Django除錯頁面有鉤子,可在出現模板錯誤時提供詳細資訊。自定義模板引擎可以使用這些掛鉤來增強向用戶顯示的回溯資訊。以下鉤子可用:

1.4.1 Template postmortem

postmortem會同TemplateDoesNotExist一起出現。它列出了在嘗試查詢給定模板時使用的模板引擎和載入器。例如,如果配置了兩個Django引擎,則postmortem將顯示為:
在這裡插入圖片描述

自定義引擎可以通過在引發TemplateDoesNotExist時傳遞backendtried引數來填充時間。使用了postmortem的後端應該在模板物件上specify an origin

1.4.2 行的上下文資訊

如果在模板解析或渲染過程中發生錯誤,Django可以顯示錯誤發生的行。例如:
在這裡插入圖片描述
自定義引擎可以填充這些資訊,通過在解析和渲染期間引發的異常上設定template_debug屬性。此屬性字典具有以下值:

鍵的屬性 含義
‘name’ 發生異常的模板的名稱。
‘message’ 異常訊息
‘source_lines’ 行之前,之後和包括髮生異常的行。這是針對上下文的,所以它不應該包含超過20行左右
‘line’ 發生異常的行。
‘before’ 引發錯誤的token之前的錯誤行上的內容
‘during’ 引發錯誤的token
‘after’ 引發錯誤的token後錯誤行上的內容
‘total’ source_lines中的行數
‘top’ source_lines中開始的行號
‘bottom’ source_lines中結束的行號

鑑於上面的模板錯誤,template_debug看起來像這樣:

{
    'name': '/path/to/template.html',
    'message': "Invalid block tag: 'syntax'",
    'source_lines': [
        (1, 'some\n'),
        (2, 'lines\n'),
        (3, 'before\n'),
        (4, 'Hello {% syntax error %} {{ world }}\n'),
        (5, 'some\n'),
        (6, 'lines\n'),
        (7, 'after\n'),
        (8, ''),
    ],
    'line': 4,
    'before': 'Hello ',
    'during': '{% syntax error %}',
    'after': ' {{ world }}\n',
    'total': 9,
    'bottom': 9,
    'top': 1,
}

1.4.3 Origin API和第三方整合

Django模板提供了一個Origin物件,通過template.origin屬性。這使得除錯資訊可以顯示在模板postmortem中,也可以顯示在第三方庫中,如Django Debug Toolbar

自定義引擎可以提供自己的template.origin資訊通過建立指定以下屬性的物件:

  • 'name':模板的完整路徑。
  • 'template_name':傳遞到模板載入方法的模板的相對路徑。
  • 'loader_name':標識用於載入模板的函式或類的可選字串,例如django.template.loaders.filesystem.Loader

2.Django 模板語言

2.1 語法

關於本節
這是Django模板語言語法的概述。有關詳情請參閱 語言的語法參考

Django模板只是一個文字文件或使用Django模板語言標記的Python字串。一些構造由模板引擎識別和解釋。主要是變數和標籤。

使用上下文呈現模板。渲染將變數替換為其值,這些值在上下文中查詢,並執行標記。其他所有內容都按原樣輸出。

Django模板語言的語法涉及一下四種結構。

2.1.1 變數

變數從上下文輸出一個值,這是一個類似於字典的物件,它將鍵對映到值。

變數被{{}}包圍:

My first name is {{ first_name }}. My last name is {{ last_name }}.

在上下文中如果存在一個這樣的字典{'first_name': 'John', 'last_name': 'Doe'},此模板呈現為:

My first name is John. My last name is Doe.

字典查詢,屬性查詢和列表索引查詢都可以使用點表示法實現:

{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}

如果變數解析為可呼叫的函式,則模板系統將呼叫它而不帶引數,並使用其結果進行渲染。


譯者例項
(1)在views.py中定義如下:

class Classval:
    def __init__(self):
        self.val = 10
        self.dicts ={"id":id(self),"val":self.val}
        self.lists = ["Classval",self.val]

    def getpow(self):
        return pow(self.val,3)

def index(request):
    cv = Classval()
    return render(request,'model_layer/index.html',locals())

(2)在model_layer/index.html中定義如下html內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
cv.val:{{ cv.val }}<hr>
cv.dicts.id:{{ cv.dicts.id }}<hr>
cv.dicts.val:{{ cv.dicts.val }}<hr>
cv.lists.0:{{ cv.lists.0 }}<hr>
cv.lists.1:{{ cv.lists.1 }}<hr>
cv.getpow:{{ cv.getpow }}<hr>
</body>
</html>

(3)輸出結果
在這裡插入圖片描述

2.1.2 標籤

標籤在渲染過程中提供任意邏輯。

這個定義是故意模糊的。例如,標籤可以輸出內容,用作控制結構,例如“if”語句或“for”迴圈,從資料庫中獲取內容,或者甚至允許訪問其他模板標籤。

標籤被{%%}包圍,比如:
{% csrf_token %}

大多數標籤接受引數:
{% cycle 'odd' 'even' %}

有些標籤需要開始和結束標籤:
{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}
譯者提供了 內建標籤 的參考以及編寫自定義標籤的說明。

2.1.1 過濾器

過濾器轉換變數和標籤引數的值。

它們看起來像這樣:
{{ django|title }}

在上下文中如果有此變數{'django': 'the web framework for perfectionists with deadlines'},此模板呈現為:

The Web Framework For Perfectionists With Deadlines
一些過濾器還接收引數:

{{ my_date|date:"Y-m-d" }}
譯者提供了內建過濾器 的參考以及 編寫自定義過濾器 的說明。

2.1.1 註釋

兩種註釋:單行註釋({# ... #})和多行註釋{% comment %}...{% endcomment %}

{# hello 單行註釋 #}

{% comment %}
多行註釋1
多行註釋2 
...
{% endcomment %}

2.2 元件

關於本節
這是Django模板語言API的概述。有關詳細資訊,請參閱API參考

2.2.1 引擎

django.template.Engine 封裝了Django模板系統的一個例項。Engine直接例項化的主要原因 是在Django專案之外使用Django模板語言。

django.template.backends.django.DjangoTemplates是一個適合Django模板後端API 的小型django.template.Engine包裝器。

2.2.1 模板

django.template.Template表示已編譯的模板。模板是用Engine.get_template()或Engine.from_string()來獲得的。

同樣,django.template.backends.django.Template是一個適合通用模板API 的小型django.template.Template包裝器。

2.2.1 上下文

django.template.Context 除了上下文資料之外還包含一些元資料。它被傳遞給Template.render()渲染模板。

django.template.RequestContext是Context儲存當前 HttpRequest和執行模板上下文處理器的子類 。

通用API沒有相同的概念。上下文資料以普通方式傳遞,如果需要,可以分開傳遞字典和當前HttpRequest。

2.2.1 載入器

模板載入器負責定位模板,載入模板和返回 Template 物件。

Django提供了 幾個內建模板載入器 並支援自定義模板載入器

2.2.1 上下文處理器

上下文處理器是一個函式,接收當前 HttpRequest為引數並返回一個字典,字典裡是要新增到呈現上下文的資料。

它們的主要用途是將所有模板共享的公共資料新增到上下文中,而不必在每個檢視中重複程式碼。

Django提供了許多內建的上下文處理器。實現自定義上下文處理器就像定義函式一樣簡單。