BZOJ-1336 [Balkan2002]Alien最小圓覆蓋(最小圓覆蓋模板)
Django基礎四之模板系統
本節目錄
一 語法
模板渲染的官方文件
關於模板渲染你只需要記兩種特殊符號(語法):
{{ }}和{% %}
變數相關的用{{}},邏輯相關的用{%%}。
二 變數
在Django的模板語言中按此語法使用:{{ 變數名 }}。
當模版引擎遇到一個變數,它將計算這個變數,然後用結果替換掉它本身。 變數的命名包括任何字母數字以及下劃線 ("_")的組合。 變數名稱中不能有空格或標點符號。
深度查詢據點符(.)在模板語言中有特殊的含義。當模版系統遇到點("."),它將以這樣的順序查詢:
字典查詢(Dictionary lookup)
屬性或方法查詢(Attribute or method lookup)
數字索引查詢(Numeric index lookup)
注意事項:
- 如果計算結果的值是可呼叫的,它將被無引數的呼叫。 呼叫的結果將成為模版的值。
- 如果使用的變數不存在, 模版系統將插入 string_if_invalid 選項的值, 它被預設設定為'' (空字串) 。
幾個例子:
view中程式碼:
def index(request): import datetime s = "hello" l = [111, 222, 333] # 列表 dic = {"name": "yuan", "age": 18} # 字典 date = datetime.date(1993, 5, 2) # 日期物件 class Person(object): def __init__(self, name): self.name = name def dream(self): return 'dreamer' person_yuan = Person("chao") # 自定義類物件 person_egon = Person("yantao") person_alex = Person("jinxin") person_list = [person_yuan, person_egon, person_alex] return render(request, "index.html", {"l": l, "dic": dic, "date": date, "person_list": person_list}) # return render(request,'index.html',locals()) #locals()獲取函式內容所有的變數,然後通過render方法給了index.html檔案進行模板渲染,如果你圖省事,你可以用它,但是很多多餘的變數也被傳進去了,效率低
模板中支援的寫法:
<h4>{{s}}</h4> <h4>列表:{{ l.0 }}</h4> <h4>列表:{{ l.2 }}</h4> <h4>字典:{{ dic.name }}</h4> <h4>日期:{{ date.year }}</h4> <!--取列表的第1個物件的name屬性的值--> <h4>類物件列表:{{ person_list.0.name }}</h4> <!--取列表的第1個物件的dream方法的返回值,如果沒有返回值,拿到的是none--> <h4>類物件列表:{{ person_list.0.dream }}</h4> 注意: 呼叫物件裡面的方法的時候,不需要寫括號來執行,並且只能執行不需要傳引數的方法,如果你的這個方法需要傳引數,那麼模板語言不支援,不能幫你渲染
注意我們直接在js程式碼中使用模板語法的時候,模板渲染的時候會有個轉義的動作,將s = ['哈哈','xx']這種資料中的元素的引號變為一個特殊符號:
<script> // 不加safe的話,引號會被轉義。
// var a = {{ s }} // var a = ['哈哈', 'xx']; // console.log(a[0]) // 加上safe就正常了 var a = {{ s|safe }}; console.log(a[0])
// 還要注意,當我們模板渲染的時候,後端返回的資料是字串的話,我們需要將{{ s }}外面加上引號
比如s = '哈哈'
js中的寫法
var a = '{{ s }}' </script>
三 過濾器
在Django的模板語言中,通過使用過濾器來改變變數的顯示。
過濾器的語法: {{ value|filter_name:引數 }}
使用管道符"|"來應用過濾器。
例如:{{ name|lower }}會將name變數應用lower過濾器之後再顯示它的值。lower在這裡的作用是將文字全都變成小寫。
注意事項:
- 過濾器支援“鏈式”操作。即一個過濾器的輸出作為另一個過濾器的輸入。
- 過濾器可以接受引數,例如:{{ sss|truncatewords:30 }},這將顯示sss的前30個詞。
- 過濾器引數包含空格的話,必須用引號包裹起來。比如使用逗號和空格去連線一個列表中的元素,如:{{ list|join:', ' }}
- '|'左右沒有空格沒有空格沒有空格
Django的模板語言中提供了大約六十個內建過濾器。
default
如果一個變數是false或者為空,使用給定的預設值。 否則,使用變數的值。
{{ value|default:"nothing"}}
如果value沒有傳值或者值為空的話就顯示nothing
length
返回值的長度,作用於字串和列表。
{{ value|length }}
返回value的長度,如 value=['a','b','c','d']的話,就顯示4.
filesizeformat
將值格式化為一個 “人類可讀的” 檔案尺寸 (例如'13 KB'
,'4.1 MB'
,'102 bytes'
, 等等)。例如:
{{ value|filesizeformat }}
如果 value 是 123456789,輸出將會是 117.7 MB。
slice
切片,如果value="hello world",還有其他可切片的資料型別
{{value|slice:"2:-1"}}
date
格式化,如果 value=datetime.datetime.now()
{{ value|date:"Y-m-d H:i:s"}}
關於時間日期的可用的引數(除了Y,m,d等等)還有很多,有興趣的可以去查檢視看。
safe
Django的模板中在進行模板渲染的時候會對HTML標籤和JS等語法標籤進行自動轉義,原因顯而易見,這樣是為了安全,django擔心這是使用者新增的資料,比如如果有人給你評論的時候寫了一段js程式碼,這個評論一提交,js程式碼就執行啦,這樣你是不是可以搞一些壞事兒了,寫個彈窗的死迴圈,那瀏覽器還能用嗎,是不是會一直彈窗啊,這叫做xss攻擊,所以瀏覽器不讓你這麼搞,給你轉義了。但是有的時候我們可能不希望這些HTML元素被轉義,比如我們做一個內容管理系統,後臺新增的文章中是經過修飾的,這些修飾可能是通過一個類似於FCKeditor編輯加註了HTML修飾符的文字,如果自動轉義的話顯示的就是保護HTML標籤的原始檔。為了在Django中關閉HTML的自動轉義有兩種方式,如果是一個單獨的變數我們可以通過過濾器“|safe”的方式告訴Django這段程式碼是安全的不必轉義。
我們去network那個地方看看,瀏覽器看到的都是渲染之後的結果,通過network的response的那個部分可以看到,這個a標籤全部是特殊符號包裹起來的,並不是一個標籤,這都是django搞得事情。
比如:
value = "<a href='#'>點我</a>" 和 value="<script>alert('123')</script>"
{{ value|safe}}
很多網站,都會對你提交的內容進行過濾,一些敏感詞彙、特殊字元、標籤、黃賭毒詞彙等等,你一提交內容,人家就會檢測你提交的內容,如果包含這些詞彙,就不讓你提交,其實這也是解決xss攻擊的根本途徑,例如部落格園:
truncatechars
如果字串字元多於指定的字元數量,那麼會被截斷。截斷的字串將以可翻譯的省略號序列(“...”)結尾。
引數:截斷的字元數
{{ value|truncatechars:9}} #注意:最後那三個省略號也是9個字元裡面的,也就是這個9截斷出來的是6個字元+3個省略號,有人會說,怎麼展開啊,配合前端的點選事件就行啦
truncatewords
在一定數量的字後截斷字串,是截多少個單詞。
例如:‘hello girl hi baby yue ma’,
{{ value|truncatewords:3}} #上面例子得到的結果是 'hello girl h1...'
cut
移除value中所有的與給出的變數相同的字串
{{ value|cut:' ' }}
如果value為'i love you',那麼將輸出'iloveyou'.
join
使用字串連線列表,{{ list|join:', ' }},就像Python的str.join(list)
timesince(瞭解)
將日期格式設為自該日期起的時間(例如,“4天,6小時”)。
採用一個可選引數,它是一個包含用作比較點的日期的變數(不帶引數,比較點為現在)。 例如,如果blog_date是表示2006年6月1日午夜的日期例項,並且comment_date是2006年6月1日08:00的日期例項,則以下將返回“8小時”:
{{ blog_date|timesince:comment_date }}
分鐘是所使用的最小單位,對於相對於比較點的未來的任何日期,將返回“0分鐘”。
timeuntil(瞭解)
似於timesince,除了它測量從現在開始直到給定日期或日期時間的時間。 例如,如果今天是2006年6月1日,而conference_date是保留2006年6月29日的日期例項,則{{ conference_date | timeuntil }}將返回“4周”。
使用可選引數,它是一個包含用作比較點的日期(而不是現在)的變數。 如果from_date包含2006年6月22日,則以下內容將返回“1周”:
{{ conference_date|timeuntil:from_date }}
這裡簡單介紹一些常用的模板的過濾器,更多詳見
四 標籤Tags
標籤看起來像是這樣的:{% tag %}
。標籤比變數更加複雜:一些在輸出中建立文字,一些通過迴圈或邏輯來控制流程,一些載入其後的變數將使用到的額外資訊到模版中。一些標籤需要開始和結束標籤 (例如{% tag %} ...
標籤 內容 ... {% endtag %})。
for標籤
遍歷每一個元素: 寫個for,然後 tab鍵自動生成for迴圈的結構,迴圈很基礎,就這麼簡單的用,沒有什麼break之類的,複雜一些的功能,你要通過js
{% for person in person_list %} <p>{{ person.name }}</p> <!--凡是變數都要用兩個大括號括起來--> {% endfor %}
可以利用{%forobjinlistreversed%}反向完成迴圈。
遍歷一個字典:
{% for key,val in dic.items %} <p>{{ key }}:{{ val }}</p> {% endfor %}
注:迴圈序號可以通過{{forloop}}顯示,必須在迴圈內部用
forloop.counter 當前迴圈的索引值(從1開始),forloop是迴圈器,通過點來使用功能 forloop.counter0 當前迴圈的索引值(從0開始) forloop.revcounter 當前迴圈的倒序索引值(從1開始) forloop.revcounter0 當前迴圈的倒序索引值(從0開始) forloop.first 當前迴圈是不是第一次迴圈(布林值) forloop.last 當前迴圈是不是最後一次迴圈(布林值) forloop.parentloop 本層迴圈的外層迴圈的物件,再通過上面的幾個屬性來顯示外層迴圈的計數等
for ... empty
for標籤帶有一個可選的{%empty%}從句,以便在給出的組是空的或者沒有被找到時,可以有所操作。
{% for person in person_list %} <p>{{ person.name }}</p> {% empty %} <p>sorry,no person here</p> {% endfor %}
if 標籤
{%if%}會對一個變數求值,如果它的值是“True”(存在、不為空、且不是boolean型別的false值),對應的內容塊會輸出。
{% if num > 100 or num < 0 %} <p>無效</p> <!--不滿足條件,不會生成這個標籤--> {% elif num > 80 and num < 100 %} <p>優秀</p> {% else %} <!--也是在if標籤結構裡面的--> <p>湊活吧</p> {% endif %}
當然也可以只有if和else
{% if user_list|length > 5 %} <!--結合過濾器來使用--> 七座豪華SUV {% else %} 黃包車 {% endif %}
if語句支援 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判斷,注意條件兩邊都有空格。
with
使用一個簡單地名字快取一個複雜的變數,多用於給一個複雜的變數起別名,當你需要使用一個“昂貴的”方法(比如訪問資料庫)很多次的時候是非常有用的
例如:
注意等號左右不要加空格。
{% with total=business.employees.count %} {{ total }} <!--只能在with語句體內用--> {% endwith %}
或
{% with business.employees.count as total %} {{ total }} {% endwith %}
csrf_token
我們以post方式提交表單的時候,會報錯,還記得我們在settings裡面的中介軟體配置裡面把一個csrf的防禦機制給登出了啊,本身不應該登出的,而是應該學會怎麼使用它,並且不讓自己的操作被forbiden,通過這個東西就能搞定。
這個標籤用於跨站請求偽造保護,
在頁面的form表單裡面(注意是在form表單裡面)任何位置寫上{% csrf_token %},這個東西模板渲染的時候替換成了<input type="hidden" name="csrfmiddlewaretoken" value="8J4z1wiUEXt0gJSN59dLMnktrXFW0hv7m4d40Mtl37D7vJZfrxLir9L3jSTDjtG8">,隱藏的,這個標籤的值是個隨機字串,提交的時候,這個東西也被提交了,首先這個東西是我們後端渲染的時候給頁面加上的,那麼當你通過我給你的form表單提交資料的時候,你帶著這個內容我就認識你,不帶著,我就禁止你,因為後臺我們django也存著這個東西,和你這個值相同的一個值,可以做對應驗證是不是我給你的token,儲存這個值的東西我們後面再學,你先知道一下就行了,就像一個我們後臺給這個使用者的一個通行證,如果你使用者沒有按照我給你的這個正常的頁面來post提交表單資料,或者說你沒有先去請求我這個登陸頁面,而是直接模擬請求來提交資料,那麼我就能知道,你這個請求是非法的,反爬蟲或者惡意攻擊我的網站,以後將中介軟體的時候我們在細說這個東西,但是現在你要明白怎麼回事,明白為什麼django會加這一套防禦。
爬蟲傳送post請求簡單模擬:
import requests res = requests.post('http://127.0.0.1:8000/login/',data={ 'username':'chao', 'password':'123' }) print(res.text)
註釋
{# ... #}
注意事項
1. Django的模板語言不支援連續判斷,即不支援以下寫法:
{% if a > b > c %} ... {% endif %}
2. Django的模板語言中屬性的優先順序大於方法(瞭解)
def xx(request): d = {"a": 1, "b": 2, "c": 3, "items": "100"} return render(request, "xx.html", {"data": d})
如上,我們在使用render方法渲染一個頁面的時候,傳的字典d有一個key是items並且還有預設的 d.items() 方法,此時在模板語言中:
{{ data.items }}
預設會取d的items key的值。
五 模板繼承
Django模版引擎中最強大也是最複雜的部分就是模版繼承了。模版繼承可以讓您建立一個基本的“骨架”模版,它包含您站點中的全部元素,並且可以定義能夠被子模版覆蓋的blocks 。
通過從下面這個例子開始,可以容易的理解模版繼承:
<!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>{% block title %}My amazing site{%/span> endblock %}</title> </head> <body> <div id="sidebar"> {% block sidebar %} <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> {% endblock %} </div> <div id="content"> {% block content %}{% endblock %} </div> </body> </html>
這個模版,我們把它叫作base.html
, 它定義了一個可以用於兩列排版頁面的簡單HTML骨架。“子模版”的工作是用它們的內容填充空的blocks。
在這個例子中,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
標籤,並用子模版中的內容來替換這些block。根據blog_entries
的值,輸出可能看起來是這樣的:
<!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>My amazing blog</title> </head> <body> <div id="sidebar"> <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> </div> <div id="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,所以系統使用了父模版中的值。父模版的{% block %}
標籤中的內容總是被用作備選內容(fallback)。
這種方式使程式碼得到最大程度的複用,並且使得新增內容到共享的內容區域更加簡單,例如,部分範圍內的導航。
這裡是使用繼承的一些提示:
-
如果你在模版中使用
{% extends %}
標籤,它必須是模版中的第一個標籤。其他的任何情況下,模版繼承都將無法工作,模板渲染的時候django都不知道你在幹啥。 -
在base模版中設定越多的
{% block %}
標籤越好。請記住,子模版不必定義全部父模版中的blocks,所以,你可以在大多數blocks中填充合理的預設內容,然後,只定義你需要的那一個。多一點鉤子總比少一點好。 -
如果你發現你自己在大量的模版中複製內容,那可能意味著你應該把內容移動到父模版中的一個
{% block %}
中。 -
If you need to get the content of the block from the parent template, the
{{ block.super }}
variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using{{ block.super }}
will not be automatically escaped (see thenext section), since it was already escaped, if necessary, in the parent template. 將子頁面的內容和繼承的母版中block裡面的內容同時保留
-
為了更好的可讀性,你也可以給你的
{% endblock %}
標籤一個名字。例如:
{% block content %} ... {% endblock content %}
在大型模版中,這個方法幫你清楚的看到哪一個 {% block %}
標籤被關閉了。
- 不能在一個模版中定義多個相同名字的
block
標籤。
六 元件
可以將常用的頁面內容如導航條,頁尾資訊等元件儲存在單獨的檔案中,然後在需要使用的地方,檔案的任意位置按如下語法匯入即可。
{% include 'navbar.html' %}
例如:有個如下的導航欄,nav.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .c1{ background-color: red; height: 40px; } </style> </head> <body> <div class="c1"> <div> <a href="">xx</a> <a href="">dd</a> </div> </div> </body> </html>
嵌入導航欄的頁面,test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% include 'nav.html' %} <h1>xxxxxxxxxx</h1> </body> </html>
元件和外掛的簡單區別
View Code七 自定義標籤和過濾器
這些我們後面用到的時候再講:
1、在settings中的INSTALLED_APPS配置當前app,不然django無法找到自定義的simple_tag.
2、在app中建立templatetags模組(模組名只能是templatetags)
3、建立任意 .py 檔案,如:my_tags.py
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 #和自定義filter類似,只不過接收更靈活的引數,沒有個數限制。 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)
4、在使用自定義simple_tag和filter的html檔案中匯入之前建立的 my_tags.py
{% load my_tags %}
5、使用simple_tag和filter(如何呼叫)
-------------------------------.html {% load xxx %} # num=12 {{ num|filter_multi:2 }} #24 {{ num|filter_multi:"[22,333,4444]" }} {% simple_tag_multi 2 5 %} 引數不限,但不能放在if for語句中 {% simple_tag_multi num 5 %}
注意:filter可以用在if、for等語句後,simple_tag不可以
{% if num|filter_multi:30 > 100 %} {{ num|filter_multi:30 }} {% endif %}
inclusion_tag
多用於返回html程式碼片段
示例:
templatetags/my_inclusion.py
from django import template register = template.Library() @register.inclusion_tag('result.html') #將result.html裡面的內容用下面函式的返回值渲染,然後作為一個元件一樣,載入到使用這個函式的html檔案裡面 def show_results(n): #引數可以傳多個進來 n = 1 if n < 1 else int(n) data = ["第{}項".format(i) for i in range(1, n+1)] return {"data": data}#這裡可以穿多個值,和render的感覺是一樣的{'data1':data1,'data2':data2....}
templates/snippets/result.html
<ul> {% for choice in data %} <li>{{ choice }}</li> {% endfor %} </ul>
templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>inclusion_tag test</title> </head> <body> {% load inclusion_tag_test %} {% show_results 10 %} </body> </html>
八 靜態檔案相關
js、css、img等都叫做靜態檔案,那麼關於django中靜態檔案的配置,我們就需要在settings配置檔案裡面寫上這寫內容:
# STATIC_URL = '/xxx/' #別名,隨便寫名字,但是如果你改名字,別忘了前面頁面裡面如果你是通過/xxx/bootstrap.css的時候,如果這裡的別名你改成了/static/的話,你前端頁面的路徑要改成/static/bootstrap.css。所以我們都是用下面的load static的方式來使用靜態檔案路徑 STATIC_URL = '/static/' #別名 STATICFILES_DIRS = [ os.path.join(BASE_DIR,'jingtaiwenjian'), #注意別忘了寫逗號,第二個引數就是專案中你存放靜態檔案的資料夾名稱 ]
目錄:別名也是一種安全機制,瀏覽器上通過除錯臺你能夠看到的是別名的名字,這樣別人就不能知道你靜態資料夾的名字了,不然別人就能通過這個資料夾路徑進行攻擊。
前端頁面引入靜態檔案的寫法,因為別名也可能會修改,所以使用路徑的時候通過load static來找到別名,通過別名對映路徑的方式來獲取靜態檔案
{% static %}
{% load static %} <img src="{% static "images/hi.jpg" %}" alt="Hi!" />
引用JS檔案時使用:
{% load static %} <script src="{% static "mytest.js" %}"></script>
某個檔案多處被用到可以存為一個變數
{% load static %} {% static "images/hi.jpg" as myphoto %} <img src="{{ myphoto }}"></img>
{% get_static_prefix %}
{% load static %} <img src="{% get_static_prefix %}images/hi.jpg" alt="Hi!" />
或者
{% load static %} {% get_static_prefix as STATIC_PREFIX %} <img src="{{ STATIC_PREFIX }}images/hi.jpg" alt="Hi!" /> <img src="{{ STATIC_PREFIX }}images/hi2.jpg" alt="Hello!" />
注意一個html檔案中寫相對路徑時需要注意的一個問題:
例子:
<form action="/login/"></form> <img src="/static/1.jpg" alt=""> 等標籤需要寫路徑的地方,如果寫的是相對路徑,那麼前置的/這個斜槓必須寫上,不然這個請求會拼接當前網頁的路徑來發送請求,就不能匹配我們的後端路徑了