在前面的學習中注意到在例子視圖中返回文本的方式有點特別。 也就是說,HTML被直接硬編碼在 Python代碼之中。css
def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
儘管上面這種方法便於解釋視圖是如何工做的,但直接將HTML硬編碼到視圖裏卻並非一個好主意。html
對頁面設計進行的任何改變都必須對 Python 代碼進行相應的修改。 站點設計的修改每每比底層 Python 代碼的修改要頻繁得多,所以若是能夠在不進行 Python 代碼修改的狀況下變動設計,那將會方便得多。 Python 代碼編寫和 HTML 設計是兩項不一樣的工做,大多數專業的網站開發環境都將他們分配給不一樣的人員(甚至不一樣部門)來完成。 設計者和HTML/CSS的編碼人員不該該被要求去編輯Python的代碼來完成他們的工做。 程序員編寫 Python代碼和設計人員製做模板兩項工做同時進行的效率是最高的,遠勝於讓一我的等待另外一我的完成對某個既包含 Python又包含 HTML 的文件的編輯工做。
基於上面這些緣由,將頁面的設計和Python的代碼分離開會更乾淨簡潔更容易維護。 咱們可使用 Django的 模板系統 (Template System)來實現這種模式。java
python的模板:HTML代碼+模板語法python
模型包括在使用時會被值替換掉的 變量,和控制模版邏輯的 標籤。linux
三種寫法:程序員
def current_time(req): # ================================原始的視圖函數 # import datetime # now=datetime.datetime.now() # html="<html><body>如今時刻:<h1>%s.</h1></body></html>" %now # ================================django模板修改的視圖函數 # from django.template import Template,Context # now=datetime.datetime.now() # t=Template('<html><body>如今時刻是:<h1>{{current_date}}</h1></body></html>') # #t=get_template('current_datetime.html') # c=Context({'current_date':str(now)}) # html=t.render(c) # # return HttpResponse(html) #另外一種寫法(推薦) import datetime now=datetime.datetime.now() return render(req, 'current_datetime.html', {'current_date':str(now)[:19]})
在 Django 模板中遍歷複雜數據結構的關鍵是句點字符。數據庫
語法:django
{{var_name}}
/templates_demo/app01/views.py:bootstrap
from django.shortcuts import render # Create your views here. def index(request): """ 模板語法只有兩個: 變量:{{}} 一、深度查詢 句點符 二、過濾器 標籤:{% %} :param request: :return: """ name = "yuan" # 定義變量 i = 10 l = [111,222,333] info = {"name":"yuan","age":22} b = True class Person(object): def __init__(self, name, age): self.name = name self.age = age alex = Person("alex", 333) egon = Person("egon", 222) person_list = [alex, egon] # return render(request,"index.html", {'name': name}) # 注意模板文件和html文件的區別:含不含模板語法 return render(request, "index.html",locals()) # 局部變量直接傳入模板,變量名一一對應
{{}}嵌套視圖傳進去的變量,若是是序列數據類型或對象能夠經過「.」把響應的值取出來。數組
/templates_demo/templates/index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>index</h1> <p>{{ name }}</p> <p>{{ i }}</p> <p>{{ info }}</p> <p>{{ l }}</p> <p>{{ alex }}</p> <p>{{ person_list }}</p> <p>{{ b }}</p> <hr> 深度查詢 <p>{{ l.1 }}</p> <p>{{ info.name }}</p> <p>{{ alex.age }}</p> <!--拿到對象的屬性--> <p>{{ person_list.1.age }}</p> <!--拿到列表中第二個對象的age屬性--> </body> </html>
運行結果:
注意:句點符也能夠用來引用對象的方法(無參數方法):
<h4>字典:{{ dic.name.upper }}</h4>
功能:把視圖傳進來的變量加以修飾後再顯示。
語法:
{{obj|filter__name:param}}
(1)default
若是一個變量是false或者爲空,使用給定的默認值。不然,使用變量的值。例如:
{{ value|default:"nothing" }}
返回值的長度。它對字符串和列表都起做用。例如:
{{ value|default:"nothing" }}
若是value是['a','b','c','d'],那麼輸出是4。
(3)filesizeformat
將值格式化爲一個 「人類可讀的」 文件尺寸 (例如 '13 KB'
, '4.1 MB'
, '102 bytes'
, 等等)。例如:
{{ value|filesizeformat }}
若是 value
是 1234345,輸出將會是 1.2 MB
。
若是 value=datetime.datetime.now()
{{ value|date:"Y-m-d" }}
若是 value="hello world",以下會輸出:llo worl
{{ value|slice:"2:-1" }}
若是字符串字符多於指定的字符數量,那麼會被截斷。截斷的字符串將以可翻譯的省略號序列(「...」)結尾。
參數:要截斷的字符數
例如:
{{ value|truncatechars:9 }}
若是value是"Joel 是 a >",輸出將會是:"Joel i ..."。
Django的模板中會對HTML標籤和JS等語法標籤進行自動轉義,緣由顯而易見,這樣是爲了安全。可是有的時候咱們可能不但願這些HTML元素被轉義,好比咱們作一個內容管理系統,後臺添加的文章中是通過修飾的,這些修飾多是經過一個相似於FCKeditor編輯加註了HTML修飾符的文本,若是自動轉義的話顯示的就是保護HTML標籤的源文件。爲了在Django中關閉HTML的自動轉義有兩種方式,若是是一個單獨的變量咱們能夠經過過濾器「|safe」的方式告訴Django這段代碼是安全的沒必要轉義。
link = '<a href=">click</a>'
直接在模板中使用link:
<p>{{ link }}</p>
在頁面中,Django默認作了轉義,<a href=">click</a>
所以必須添加safe過濾器:
<p>{{ link|safe }}</p>
代碼示例:
from django.shortcuts import render # Create your views here. def index(request): """ 模板語法只有兩個: 變量:{{}} 一、深度查詢 句點符 二、過濾器 標籤:{% %} :param request: :return: """ name = "yuan" # 定義變量 i = 10 l = [111,222,333] info = {"name":"yuan","age":22} b = True class Person(object): def __init__(self, name, age): self.name = name self.age = age alex = Person("alex", 333) egon = Person("egon", 222) person_list = [alex, egon] people_list = [] ####################### import datetime now = datetime.datetime.now() file_size= 1234345 value = "hello world" text = "hello python hi luffycity go java linux" link = '<a href=">click</a>' # return render(request,"index.html", {'name': name}) # 注意模板文件和html文件的區別:含不含模板語法 return render(request, "index.html",locals()) # 局部變量直接傳入模板,變量名一一對應
/templates_demo/templates/index.html:
<h3>過濾器</h3> <p>{{ now }}</p> <p>{{ now|date:"Y-m-d" }}</p> <p>{{ people_list|default:"數據爲空" }}</p> <p>{{ person_list|length }}</p> <p>{{ file_size|filesizeformat }}</p> <p>{{ value|slice:"2:-1" }}</p> <p>{{ text|truncatechars:9 }}</p> <p>{{ link }}</p> <p>{{ link|safe }}</p>
在頁面中展現效果:
這裏簡單介紹一些經常使用的模板的過濾器,更多詳見
標籤看起來像是這樣的: {% tag %}
。標籤比變量更加複雜:一些在輸出中建立文本,一些經過循環或邏輯來控制流程,一些加載其後的變量將使用到的額外信息到模版中。
一些標籤須要開始和結束標籤 (例如{% tag %} ...
標籤 內容 ... {% endtag %})。
(1)遍歷每個元素:
{% for i in l %} <p>{{ i }}</p> {% endfor %}
利用{% for obj in list reversed %}反向完成循環
{% for i in l reversed %} <p>{{ i }}</p> {% endfor %}
(2)遍歷一個字典:views.py中有:info = {"name":"yuan","age":22}
{% for i in info %} <p>{{ i }}</p> {% endfor %}
頁面顯示:name:yuan age:22
(3)遍歷對象數組
{% for person in person_list %} <p>{{ person.name }} {{ person.age }}</p> {% endfor %} {# forloop.counter 序號,默認從1開始計數 counter0的序號,從0開始計數 #} {% for person in person_list %} <p>{{ forloop.counter0 }} {{ person.name }} {{ person.age }}</p> {% endfor %}
頁面顯示:alex 333 egon 222
0 alex 333 1 egon 222
注:循環序號能夠經過{{forloop}}顯示
forloop.counter The current iteration of the loop (1-indexed) forloop.counter0 The current iteration of the loop (0-indexed) forloop.revcounter The number of iterations from the end of the loop (1-indexed) forloop.revcounter0 The number of iterations from the end of the loop (0-indexed) forloop.first True if this is the first time through the loop forloop.last True if this is the last time through the loop
for 標籤帶有一個可選的{% empty %} 從句,以便在給出的組是空的或者沒有被找到時,能夠有所操做。
如:在view.py中建立:empty_list = []
{% for n in empty_list%} <p>{{ forloop.counter0 }} {{ n.name }} {{ n.age }}</p> {% empty %} <p>列表爲空</p> {% endfor %}
頁面顯示:列表爲空。
{% if %}會對一個變量求值,若是它的值是「True」(存在、不爲空、且不是boolean類型的false值),對應的內容塊會輸出。用於在模板中進行邏輯判斷。
{% if num > 100 or num < 0 %} <p>無效</p> {% elif num > 80 and num < 100 %} <p>優秀</p> {% else %} <p>湊活吧</p> {% endif %}
示例:
{% if user %} <p> <a href="">hi {{ user }}</a> <a href="">註銷</a> </p> {% else %} <p> <a href="">登陸</a> <a href="">註冊</a> </p> {% endif %}
使用一個簡單的名字緩存一個複雜的變量,當你須要使用一個「昂貴的」方法(好比訪問數據庫)不少次的時候是很是有用的。(替換名字)
{{ person_list.1.name }} {% with person_list.1.name as p_l %} {{ p_l }} {% endwith %}
例如上例這種,因爲深度查詢很是長,用一個簡單的名字替換。
這個標籤用於跨站請求僞造保護。{% csrf_token %}能夠渲染出一個input標籤。點擊提交時,會提交給服務器驗證經過校驗。以下圖所示:
urls.py:
from django.contrib import admin from django.urls import path, re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index), # 含正則的時候須要使用re_path path('login/', views.login) ]
app01/views.py:
from django.shortcuts import render,HttpResponse # Create your views here. def login(request): if request.method == "POST": return HttpResponse("OK") return render(request, "login.html")
login.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> {% csrf_token %} <input type="text" name="user"> <input type="submit"> </form> </body> </html>
1)沒有添加csrf_token時,輸入信息點擊提交
2)添加了csrf_token後:
否則django沒法找到自定義的simple_tag
注意:模塊名只能是templatetags
Django去找自定義標籤的時候,就固定會在註冊的app中尋找templatetags的package.
如建立:my_tags_filter.py
from django import template register = template.Library() # register不能變 @register.filter def multi_filter(x,y): # 新過濾器,實現相乘 return x*y @register.simple_tag def multi_tag(x,y): # 新標籤 return x**y @register.simple_tag def multi_tag_higher(x, y, z): return x*y*z from django.utils.safestring import mark_safe @register.simple_tag def my_input(id,arg): result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result)
{% load my_tags_filter %}
<h4>自定義過濾器</h4> {% load my_tags_filter %} <p>{{ i|multi_filter:20 }}</p> <h4>自定義標籤</h4> <p>{% multi_tag 4 2 %}</p> <p>{% multi_tag_higher 3 2 4 %}</p> {# 過濾器最多隻能傳兩個形參;自定義標籤沒有數量限制 #} {# 標籤不能進行邏輯判斷 #} {% if i|multi_filter:10 > 50 %} <p>1010</p> {% else %} <p>{{ i }}</p> {% endif %}
注意:過濾器({{ }})能夠用再if等語句後,標籤({% %})不能夠進行邏輯判斷。
{% if num|filter_multi:30 > 100 %} {{ num|filter_multi:30 }} {% endif %}
Django模版引擎中最強大也是最複雜的部分就是模版繼承了。模版繼承可讓您建立一個基本的「骨架」模版,它包含您站點中的所有元素,而且能夠定義可以被子模版覆蓋的 blocks 。
仍是用templates_demo這個項目爲例來理解模板繼承:
利用模板加載機制的內建模板標籤: {% include %} 。該標籤容許在(模板中)包含其它的模板的內容。 標籤的參數是所要包含的模板名稱,能夠是一個變量,也能夠是用單/雙引號硬編碼的字符串。 每當在多個模板中出現相同的代碼時,就應該考慮是否要使用 {% include %} 來減小重複。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style type="text/css"> * { margin: 0; padding: 0; } .header{ width: 100%; height: 50px; background-color: #369; } </style> </head> <body> <div class="header"></div> <div class="container"> <div class="row"> <div class="col-md-3"> {% include 'advertise.html' %} </div> <div class="col-md-9"> <h1>index</h1> <p>{{ name }}</p> <p>{{ i }}</p> <p>{{ info }}</p> <p>{{ l }}</p> <p>{{ alex }}</p> <p>{{ person_list }}</p> <p>{{ b }}</p> <hr>深度查詢 <p>{{ l.1 }}</p> <p>{{ info.name }}</p> <p>{{ alex.age }}</p> <!--拿到對象的屬性--> <p>{{ person_list.1.age }}</p> <!--拿到列表中第二個對象的age屬性--> <h3>過濾器</h3> <p>{{ now }}</p> <p>{{ now|date:"Y-m-d" }}</p> <p>{{ people_list|default:"數據爲空" }}</p> <p>{{ person_list|length }}</p> <p>{{ file_size|filesizeformat }}</p> <p>{{ value|slice:"2:-1" }}</p> <p>{{ text|truncatechars:9 }}</p> <p>{{ link }}</p> <p>{{ link|safe }}</p> <h4>for標籤</h4> {% for i in l reversed %} <p>{{ i }}</p> {% endfor %} {% for i, j in info.items %} <p>{{ i }}:{{ j }}</p> {% endfor %} {% for person in person_list %} <p>{{ person.name }} {{ person.age }}</p> {% endfor %} {# forloop.counter 序號,默認從1開始計數 counter0的序號,從0開始計數 #} {% for person in person_list %} <p>{{ forloop.counter0 }} {{ person.name }} {{ person.age }}</p> {% endfor %} {% for n in empty_list%} <p>{{ forloop.counter0 }} {{ n.name }} {{ n.age }}</p> {% empty %} <p>列表爲空</p> {% endfor %} <hr> {% if user %} <p> <a href="">hi {{ user }}</a> <a href="">註銷</a> </p> {% else %} <p> <a href="">登陸</a> <a href="">註冊</a> </p> {% endif %} <hr> {{ person_list.1.name }} {% with person_list.1.name as p_l %} {{ p_l }} {% endwith %} <hr> <h4>自定義過濾器</h4> {% load my_tags_filter %} <p>{{ i|multi_filter:20 }}</p> <h4>自定義標籤</h4> <p>{% multi_tag 4 2 %}</p> <p>{% multi_tag_higher 3 2 4 %}</p> {# 過濾器最多隻能傳兩個形參;自定義標籤沒有數量限制 #} {# 標籤不能進行邏輯判斷 #} {% if i|multi_filter:10 > 50 %} <p>1010</p> {% else %} <p>{{ i }}</p> {% endif %} </div> </div> </div> </body> </html>
利用模板裏的{% include 'advertise.html' %} 引入advertise.html
<div class="action"> <div class="panel panel-danger"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> Panel content 111 </div> </div> <div class="panel panel-warning"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> Panel content 222 </div> </div> <div class="panel panel-success"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> Panel content 333 </div> </div> </div>
運行後瀏覽器效果以下所示:
(1)
/templates/base.html: 通常都是用base.html
定義了一個能夠用於兩列排版頁面的簡單HTML骨架:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style type="text/css"> * { margin: 0; padding: 0; } .header{ width: 100%; height: 50px; background-color: #369; } </style> </head> <body> <div class="header"></div> <div class="container"> <div class="row"> <div class="col-md-3"> {% include 'advertise.html' %} </div> <div class="col-md-9"> {% block con %} {% endblock %} </div> </div> </div> </body> </html>
「子模版」的工做是用它們的內容填充空的blocks。block
告訴模版引擎: 子模版可能會覆蓋掉模版中的這些位置。
{% extends 'base.html' %} {% block con %} <h1>index</h1> <p>{{ name }}</p> <p>{{ i }}</p> <p>{{ info }}</p> <p>{{ l }}</p> <p>{{ alex }}</p> <p>{{ person_list }}</p> <p>{{ b }}</p> <hr>深度查詢 <p>{{ l.1 }}</p> <p>{{ info.name }}</p> <p>{{ alex.age }}</p> <!--拿到對象的屬性--> <p>{{ person_list.1.age }}</p> <!--拿到列表中第二個對象的age屬性--> <h3>過濾器</h3> <p>{{ now }}</p> <p>{{ now|date:"Y-m-d" }}</p> <p>{{ people_list|default:"數據爲空" }}</p> <p>{{ person_list|length }}</p> <p>{{ file_size|filesizeformat }}</p> <p>{{ value|slice:"2:-1" }}</p> <p>{{ text|truncatechars:9 }}</p> <p>{{ link }}</p> <p>{{ link|safe }}</p> <h4>for標籤</h4> {% for i in l reversed %} <p>{{ i }}</p> {% endfor %} {% for i, j in info.items %} <p>{{ i }}:{{ j }}</p> {% endfor %} {% for person in person_list %} <p>{{ person.name }} {{ person.age }}</p> {% endfor %} {# forloop.counter 序號,默認從1開始計數 counter0的序號,從0開始計數 #} {% for person in person_list %} <p>{{ forloop.counter0 }} {{ person.name }} {{ person.age }}</p> {% endfor %} {% for n in empty_list%} <p>{{ forloop.counter0 }} {{ n.name }} {{ n.age }}</p> {% empty %} <p>列表爲空</p> {% endfor %} <hr> {% if user %} <p> <a href="">hi {{ user }}</a> <a href="">註銷</a> </p> {% else %} <p> <a href="">登陸</a> <a href="">註冊</a> </p> {% endif %} <hr> {{ person_list.1.name }} {% with person_list.1.name as p_l %} {{ p_l }} {% endwith %} <hr> <h4>自定義過濾器</h4> {% load my_tags_filter %} <p>{{ i|multi_filter:20 }}</p> <h4>自定義標籤</h4> <p>{% multi_tag 4 2 %}</p> <p>{% multi_tag_higher 3 2 4 %}</p> {# 過濾器最多隻能傳兩個形參;自定義標籤沒有數量限制 #} {# 標籤不能進行邏輯判斷 #} {% if i|multi_filter:10 > 50 %} <p>1010</p> {% else %} <p>{{ i }}</p> {% endif %} {% endblock %}
extends
標籤是這裏的關鍵。它告訴模版引擎,這個模版「繼承」了另外一個模版。當模版系統處理這個模版時,首先,它將定位父模版——在此例中,就是「base.html」。
模版引擎將注意到 base.html
中的 block
標籤,並用子模版中的內容來替換這些block。再次訪問頁面:
同時若是想定製一個和父模板基本相同的頁面如order.html:
{% extends 'base.html' %} {% block con %} <h3>訂單</h3> {% endblock %}
顯示效果以下:
在base.html中能夠定義多個block方便繼承的頁面擴寫內容,解決http頁面代碼複用性的問題。好比把titile標籤改成block:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {% block title %} {% endblock %} <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style type="text/css"> * { margin: 0; padding: 0; } .header{ width: 100%; height: 50px; background-color: #369; } </style> </head> <body> <div class="header"></div> <div class="container"> <div class="row"> <div class="col-md-3"> {% include 'advertise.html' %} </div> <div class="col-md-9"> {% block con %} {% endblock %} </div> </div> </div> </body> </html>
{% extends 'base.html' %} {% block title %} <titile>orders</titile> {% endblock %} {% block con %} <h3>訂單</h3> {% endblock %}
請注意,子模版並無定義 sidebar
block,因此係統使用了父模版中的值。父模版的 {% block %}
標籤中的內容老是被用做備選內容(fallback)。
這種方式使代碼獲得最大程度的複用,而且使得添加內容到共享的內容區域更加簡單,例如,部分範圍內的導航。
1)若是你在模版中使用 {% extends %}
標籤,它必須是模版中的第一個標籤。其餘的任何狀況下,模版繼承都將沒法工做。(將{% extends 'base.html' %}寫在首行)
2)在base模版中設置越多的 {% block %}
標籤越好。請記住,子模版沒必要定義所有父模版中的blocks,因此,你能夠在大多數blocks中填充合理的默認內容,而後,只定義你須要的那一個。多一點鉤子總比少一點好。
3)若是你發如今模版中有大量的複製內容,那可能意味着你應該把內容移動到父模版中的一個 {% block %}
中。
4)若是須要從父模板中獲取block中的內容,可使用{{ block.super }}拿到父模板的內容,並繼續續寫。
{% extends 'base.html' %} {% block title %} <titile>orders</titile> {% endblock %} {% block con %} {{ block.super }} <h3>訂單</h3> {% endblock %}
base.html:
<div class="col-md-9"> {% block con %} <h4>content</h4> {% endblock %} </div>
訪問網頁效果以下:
5)爲了更好的可讀性,你也能夠給你的 {% endblock %}
標籤一個 名字 。例如:
{% block content %} ... {% endblock content %}
在大型模版中,這個方法幫你清楚的看到哪個 {% block %}
標籤被關閉了。
6)不能在一個模版中定義多個相同名字的 block
標籤。
include有功能extend都有,但extend擴展盒子的功能include不具有。所以extend功能更增強大,推薦在模板複用的時候使用繼承關係。