模板渲染首先有一個模板對象Template,而後有一個上下文對象Context,經過render方法進行渲染,最後返回字符串,render方法的本質仍是調用了HttpResponse方法。css
>>> python manange.py shell (進入該django項目的環境) >>> from django.template import Context, Template >>> t = Template('My name is {{ name }}.') >>> c = Context({'name': 'shenjianping'}) >>> t.render(c) 'My name is shenjianping.' # 同一模板,多個上下文,一旦有了模板對象,能夠經過它渲染多個context # 固然使用同一模板源渲染多個context,只進行 一次模板建立,而後屢次調用render方法渲染會更爲高效:
# 每次循環都建立了模板 for name in ('aa', 'bb'): t = Template('Hello, {{ name }}') print(t.render(Context({'name': name}))) # 只建立一次模板,更爲高效 t = Template('Hello, {{ name }}') for name in ('aa', 'bb'): print(t.render(Context({'name': name})))
Engine類位於django.template.Engine,這個類主要是將settings中模板的配置讀入:html
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
在Engine類的初始化方法中:python
class Engine: ... def __init__(self, dirs=None, app_dirs=False, context_processors=None, debug=False, loaders=None, string_if_invalid='', file_charset='utf-8', libraries=None, builtins=None, autoescape=True): if dirs is None: dirs = [] if context_processors is None: context_processors = [] if loaders is None: loaders = ['django.template.loaders.filesystem.Loader'] if app_dirs: loaders += ['django.template.loaders.app_directories.Loader'] if not debug: loaders = [('django.template.loaders.cached.Loader', loaders)] else: if app_dirs: raise ImproperlyConfigured( "app_dirs must not be set when loaders is defined.") if libraries is None: libraries = {} if builtins is None: builtins = [] self.dirs = dirs self.app_dirs = app_dirs self.loaders = loaders ...
根據傳遞的app_dirs的布爾值獲取加載模板的類loaders:jquery
django.template.loaders.app_directories.Loader
另外,Engine類提供了獲取默認的模板引擎的方法:shell
def get_default(): """ Return the first DjangoTemplates backend that's configured, or raise ImproperlyConfigured if none are configured. This is required for preserving historical APIs that rely on a globally available, implicitly configured engine such as: >>> from django.template import Context, Template >>> template = Template("Hello {{ name }}!") >>> context = Context({'name': "world"}) >>> template.render(context) 'Hello world!' """ # Since Engine is imported in django.template and since # DjangoTemplates is a wrapper around this Engine class, # local imports are required to avoid import loops. from django.template import engines from django.template.backends.django import DjangoTemplates for engine in engines.all(): if isinstance(engine, DjangoTemplates): return engine.engine raise ImproperlyConfigured('No DjangoTemplates backend is configured.')
在settings.py文件的配置中,傳入的app_dirs=True,因此使用的loaders是:數據庫
django.template.loaders.app_directories.Loader
在這裏它作這麼幾件事:django
()
就像get_template()
,除了它採用模板名稱列表,好比select_template([index.html,user.html])。它按順序嘗試每一個名稱並返回存在的第一個模板。三、Templatebootstrap
class DjangoTemplates(BaseEngine): app_dirname = 'templates' ... def get_template(self, template_name): try: return Template(self.engine.get_template(template_name), self) except TemplateDoesNotExist as exc: reraise(exc, self) ...
class Template: def __init__(self, template, backend): self.template = template self.backend = backend @property def origin(self): return self.template.origin def render(self, context=None, request=None): context = make_context(context, request, autoescape=self.backend.engine.autoescape) try: return self.template.render(context) except TemplateDoesNotExist as exc: reraise(exc, self.backend)
注意這個render是django.shortcuts.render不是Template中的renderapp
def render(request, template_name, context=None, content_type=None, status=None, using=None): """ Return a HttpResponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments. """ content = loader.render_to_string(template_name, context, request, using=using) return HttpResponse(content, content_type, status)
調用loader中的render_to_string方法,而這個方法返回的就是上述Template類中render方法的返回值,可是最後仍是以HttpResponse的方法返回內容函數
def render_to_string(template_name, context=None, request=None, using=None): """ Load a template and render it with a context. Return a string. template_name may be a string or a list of strings. """ if isinstance(template_name, (list, tuple)): template = select_template(template_name, using=using) else: template = get_template(template_name, using=using) return template.render(context, request)
這就是使用render渲染的簡單過程。
總結:
變量從上下文輸出一個值,這是一個相似於dict的對象,它將鍵映射到值,在html中進行渲染時使用{{}}來渲染變量。
from django.shortcuts import render,HttpResponse def test(request): return render(request,'index.html',{'first_name': 'John', 'last_name': 'Doe'})
在index.html渲染:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ first_name }} {{ last_name }} </body> </html>
def test(request): #字典 dict = {'first_name': 'John', 'last_name': 'Doe'} #列表 list = ['first_name','last_name'] #對象 book_obj = models.Book.objects.get(id=2) return render(request,'index.html',{'dict':dict,'list':list,'book_obj':book_obj})
在index.html渲染:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--字典--> {{ dict.keys }}{{ dict.values }} <!--列表--> {{ list.0 }} <!--對象--> {{ book_obj.title }} </body> </html>
使用大括號與%的組合進行表示{% tag %},標籤在渲染過程當中提供任意邏輯。例如,標籤能夠輸出內容,用做控制結構(if或for結構),從數據庫中獲取內容,甚至容許訪問其餘模板標籤。
該標籤計算一個變量,若是該變量是「真」(即存在,不爲空,而且不是假布爾值)的數據塊的內容被輸出:{% if %}
{% if num >= 150 %} {% if num > 200 %} <p>num大於200</p> {% else %} <p>num大於100小於200</p> {% endif %} {% elif num < 150 %} <p>num小於150</p> {% else %} <p>num等於150</p> {% endif %}
注意:
(1){% if %}語句以{% endif %}結尾;
(2){% if %}容許在同一標記中使用both and
和or
子句, and
優先級高於or,
例如:
{% if user_list and role_list or depart_list %} #這將被解釋爲: if (user_list and role_list) or depart_list:
(3){% if %} 標籤接受and,or或者not來測試多個變量值或者否認一個給定的變量;
{% if not user_list or role_list %} There are no usersor there are some roles. {% endif %} {% if user_list and not role_list %} There are some usersor and no roles. {% endif %}
(4){% if %}標籤可使用運算符 ==(等於)
,!=(不等於)
,<(小於)
,>(大於)
, <=(小於等於)
,>=(大於等於)
,in(在...中)
(1)遍歷列表
循環遍歷列表中的每一個item,使item在上下文變量中可用。
{% for item in list %} <!--循環列表--> {{ item }} <!--取出列表中的每個值--> {% endfor %}
固然,也能夠反向循環列表,加入reversed參數便可:
{% for item in list reversed %} <!--反向循環列表--> {{ item }} <!--取出列表中的每個值--> {% endfor %}
(2)遍歷字典
{% for %}標籤支持遍歷字典:
{% for key, value in dict.items %}
{{ key }}: {{ value }}
{% endfor %}
若是直接遍歷,獲得的value值:
{% for item in dict %} {{ item }} <!--value值--> {% endfor %}
(3)forloop
在遍歷過程當中,系統不支持中斷循環,也不支持continue語句,可是{% for %}標籤內置了一個forloop模板變量,這個能夠提供一些循環信息:
#for循環設置循環中可用的許多變量: 變量 描述 forloop.counter 循環的當前迭代(1索引) forloop.counter0 循環的當前迭代(0索引) forloop.revcounter 循環結束時的迭代次數(1索引) forloop.revcounter0 循環結束時的迭代次數(0索引) forloop.first 若是這是第一次經過循環,則爲真 forloop.last 若是這是最後一次循環,則爲真 forloop.parentloop 對於嵌套循環,這是圍繞當前循環的循環
變量的簡單使用:
#forloop.counter {% for item in list %} {{ item }}{{ forloop.counter }} {% endfor %} #forloop.first 若是這是第一次經過循環,則爲真,在某些狀況下仍是頗有用的,好比加入樣式 {% for user in user_list%} {% if forloop.first %}
<li class="first">
{% else %}
<li>
{% endif %} {{ user}} </li> {% endfor %}
注意:forloop變量只能在循環中獲得,當模板解析器到達{% endfor %}時forloop就消失了
(4){% for %}標籤能夠嵌套
#{%for%}之間的嵌套 {% for country in countries %} {{ country.title}} <ul> {% for city in country.city_list %} <li>{{ city }}</li> {% endfor %} </ul> {% endfor %} #{%for%}與{%if%}之間的嵌套 {% for country in countries %} {% if country.title =='abx' %} {{ country}} {% endif %} {% endfor %}
該for
標籤可使用一個可選選項,若是給定的列表爲空或沒法找到,就顯示該標籤下的內容
{% for i in list %} <li>{{ forloop.counter }}----{{ i }}</li> {% empty %} <li>this is empty!</li> {% endfor %} <!-- 1----1 2----2 3----3
若是list爲空列表,就顯示this is empty! -->
用於生成csrf_token驗證碼,用於防止跨站攻擊驗證,這裏會生成一個隱藏的input標籤,包含鍵值對一塊兒提交給後臺驗證。
#使用方式 #在任何使用POST表單的模板中,對於內部url,使用元素csrf_token標記<form>,例如: <form method="post"> {% csrf_token %} ... </form>
注意:後臺若是使用render_to_response()方法,不會生效。
返回與給定視圖和可選參數匹配的絕對路徑引用(不帶域名的URL)
{% url 'some-url-name' v1 v2 %} #第一個參數是URL模式名稱。它能夠是帶引號的文字或任何其餘上下文變量。其餘參數是可選的,應該是以空格分隔的值,這些值將用做URL中的參數。上面的例子顯示了傳遞位置參數。
或者,可使用關鍵字語法: {% url 'some-url-name' arg1=v1 arg2=v2 %} #不要在一次調用中混合使用位置和關鍵字語法。應該存在URLconf所需的全部參數。
例如:
#路由
path('user/<int:id>/', views.user,name='user-detail'),
#表單 <form action="{% url "user-detail" user.id%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
以更簡單的名稱代替複雜變量。
{% with total=book.authors.count %} {{ total }} {% endwith %} #或者這樣書寫 {% with book.authors.count as total %} {{ total }} {% endwith %}
固然也支持多個上下文變量:
{% with x=abc y=jkl %} {{x}}--{{y}} {% endwith %}
中止模板引擎渲染此塊標記的內容
#傳遞變量 def index(request): k='123' return render(request,'index.html',locals()) #不會渲染m變量 {% verbatim %} {{ k }} {% endverbatim %} #頁面效果 {{k}}
#要控制模板的自動轉義 {{ value}} #若是value爲<a href="#">跳轉</a>,輸出將是<a href="#">跳轉</a> #假如使用autoescape標籤 {% autoescape off %} {{ value }} {% endautoescape %} #輸出將是「跳轉」
更多請查看:https://docs.djangoproject.com/zh-hans/2.0/ref/templates/builtins/#built-in-tag-reference
過濾器經過修改要顯示的變量,從而使變量的值變成所指望的值。
#語法格式: {{obj|filter:param}}
注意:冒號(:)後只能傳一個參數,可是過濾器能夠用於控制語句中,如{% if ****%}{% endif %},因此用的頻率較高。
#給變量加上相應的值 {{ value|add:'6' }}
此過濾器將首先嚐試將兩個值強制轉換爲整數。若是失敗,它將嘗試將值一塊兒添加到一塊兒。
# 給變量中的引號前加上斜線 {{ value|addslashes}} #若是value爲:"I'm using Django",輸出將是"I\'m using Django"
#首字母大寫 若是value是"django",輸出將是"Django"。 {{ value|capfirst }}
# 從字符串中移除指定的字符 {{ value|cut:" " }} #移除空格 #若是value是"String with spaces",輸出將是 "Stringwithspaces"
#格式化日期字符串 import datetime value=datetime.datetime.now() {{ value|date:'Y-m-d' }}
# 若是值是False,就替換成設置的默認值,不然就是用原本的值 {{ value|default:"nothing" }} #若是value是""(空字符串),則輸出爲nothing。
#若是值是None,就替換成設置的默認值,不然就使用原本的值 {{ value|default_if_none:"nothing" }} #若是value是None,輸出將是nothing。
#轉義字符串的HTML。具體來講,它使這些替換: < 轉換爲 < > 轉換爲 > ' (單引號)轉換爲 ' " (雙引號)轉換爲 " & 轉換爲 & #使用 {% autoescape off %} {{ title|escape }} {% endautoescape %}
#返回列表中的第一個項目 {{ value|first }} #若是value是列表['a','b','c','d'],則輸出將是'a'
#返回列表中的最後一個項目 {{ value|first }} #若是value是列表['a','b','c','d'],則輸出將是'd'
#返回列表的長度 {{ value|length}} #若是value是列表['a','b','c','d'],則輸出將是4
#若是在沒有參數的狀況下使用,則將浮點數舍入到一個小數位 - 但前提是要顯示小數部分 {{ value|floatformat }} #若是value爲12.21,輸出將是12.2 #若是value爲12.00,輸出將是12 #若是value爲12.71,輸出將是12.3
#使用字符串鏈接列表,如Python str.join(list) {{ value|join:" // " }} #若是value是列表,則輸出將是字符串 。['a', 'b', 'c']"a // b // c"
#將字符串所有轉換爲小寫 {{ value|lower }} #若是value是ABC,輸出將是abc
注意:與之相對的是upper
#轉義值以在URL中使用 {{ value|urlencode }} #若是value是"http://www.yw.com/?page=1&pageSize=3",輸出將是 "http%3A//www.yw.com/%3Fpage%3D1%26pageSize%3D3"
更多詳情參見:https://docs.djangoproject.com/zh-hans/2.0/ref/templates/builtins/#ref-templates-builtins-filters
(四)自定義過濾器和標籤
自定義標籤應按照如下步驟執行:
注意:在建立templatetags目錄不要忘了__init__.py
文件,以確保該目錄做爲一個Python的包裝處理
from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改變 #自定製過濾器 @register.filter def filter_multi(v1,v2): return v1 * v2 #自定製標籤 @register.simple_tag def simple_tag_multi(v1,v2): return v1 * v2 @register.simple_tag def my_input(id,arg): result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result)
#注意放在html文件的第一行 {% load my_tags %}
{% load ‘my_tags’%} <!--首行--> {{ num|filter_multi:3 }} <!--假設num:12,輸出則是:36--> {% simple_tag_multi 3 10 %} <!--參數不限,但不能放在if for語句中--> {% simple_tag_multi num 5 %} <!--假設num:12,輸出則是:60-->
注意:過濾器能夠用於if等控制語句中,可是標籤不行
{% if price|filter_price:10 > 400 %}
{{ price|filter_price:20 }}
{% endif %}
更多請參考:https://docs.djangoproject.com/zh-hans/2.0/howto/custom-template-tags/
模板繼承主要是爲了提升代碼的重用性,減小開發和維護人員的壓力。
若是發如今多個html文件中有不少的代碼是重複,就能夠將這些共同的代碼提取出來,組成一個公共模板(能夠稱之爲父模板),其它不一樣的地方只須要預留鉤子,好比:
{%block 名稱%}
預留區域,能夠編寫默認內容,也能夠沒有默認內容
{%endblock 名稱%}
注意:字模板繼承的名稱應與父模板保持一致,而且不能有重名。
base.html:
{% load staticfiles %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>yw</title> <link rel="stylesheet" href="{% static 'stark/plugins/bootstrap/css/bootstrap.css' %} "/> <link rel="stylesheet" href="{% static 'stark/plugins/font-awesome/css/font-awesome.css' %} "/> <style> body { margin: 0; } </style> {% block css %}{% endblock %}<!--預留css--> </head> <body> <div class="pg-header"> </div> <div class="pg-body"> <div class="left-menu"> </div> <div class="right-body"> {% block content %} {% endblock %}<!--預留內容--> </div> </div> <script src="{% static 'stark/js/jquery-3.3.1.min.js' %} "></script> <script src="{% static 'stark/plugins/bootstrap/js/bootstrap.js' %} "></script> {% block js %} {% endblock %} <!--預留js--> </body> </html>
在繼承父模板時,須要使用標籤extends而且將其寫在子模板文件的第一行。
{% extends "父模板路徑"%}
在繼承時,能夠不用填充父模板中的預留區域,此時使用的就是默認內容;固然也能夠將預留block進行填充,若是在填充後仍然須要默認內容能夠進行獲取,經過:
{% block content %} 填充實際內容 {{block.super}}<!--用於獲取父模板預留block content中的內容-->
{% endblock %}
index.html:
{% extends 'base.html' %}<!--繼承父模板--> {% load staticfiles %} {% block css %} <style> textarea{ min-height: 200px; } </style> {% endblock %} {% block content %} <form class="change" method="post" novalidate> {% csrf_token %} </form> </div> {% endblock %} {% block js %} <script src="{% static "stark/js/m2m.js" %}"></script> <script src="{% static "stark/js/pop.js" %}"></script> {% endblock %}