django修身大法之美麗誘惑
模板
模板⽤於快速⽣成動態⻚⾯返回給客戶端,模板是⼀個⽂本,⽤於分離⽂檔的表現
形式和內容。 模板定義了佔位符以及各種⽤於規範⽂檔該如何顯示的模板標籤。
模板通常⽤於產⽣HTML,但是Django的模板也能產⽣任何基於⽂本格式的⽂檔。
模板包含兩部分:
- html程式碼
- 模板標籤
⼀、模板位置
-
在應⽤中建⽴templates⽬錄,好處不需要註冊,不好的地⽅,有多個應⽤的
時候不能復⽤⻚⾯ -
第⼆種是放在⼯程的⽬錄下,好處是如果有多個應⽤,可以調⽤相同的⻚⾯,
需要註冊- 需要修改項⽬的配置⽂件settings.py
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 這裡預設使用Django自帶的模板引擎 'DIRS': [os.path.join(BASE_DIR, 'templates')] # 模板絕對路徑,如果是自己手動建立的django專案,則該內容為空,需要手動設定 , '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', ], }, }, ]
Django 模板查詢機制: Django首先會查詢DIRS下配置的模板路徑,如果不在的話,則是在每個 app 的 templates
⽂件夾中找(⽽不只是當前 app 中的程式碼只在當前的 app 的 templates ⽂件
夾中找)。各個 app 的 templates 形成⼀個⽂件夾列表,Django 遍歷這個列
表,⼀個個⽂件夾進⾏查詢,當在某⼀個⽂件夾找到的時候就停⽌,所有的都
遍歷完了還找不到指定的模板的時候就是 Template Not Found (過程類似於
Python找包)。這樣設計有利當然也有弊,有利是的地⽅是⼀個app可以⽤另
⼀個app的模板⽂件,弊是有可能會找錯了。所以我們使⽤的時候在
templates 中建⽴⼀個 app 同名的⽂件夾,這樣就好了。
⼆、模板的渲染
2.1 loader載入
好處是可以載入⼀次模板,然後進⾏多次渲染
from django.template import loader #導⼊loader
def index(request):
temp = loader.get_template('index.html')
# 渲染模板,⽣出html原始碼
res = temp.render(context={'content':'hello index'})
print(res)
return HttpResponse(res)
2.2 render
from django.shortcuts import render render(request,templatesname,context=None) 引數: request:請求物件 templatesname:模板名稱 context:引數字典,必須是字典
三、模板語法
django模板中包括兩部分:變數和內建標籤。變數會在模板渲染時被其值代替,
內建標籤負責邏輯控制。
3.1 變數
變數在模板中的表示為:{{ 變數名 }},變數名就是render中context中的鍵。變數
可以基本型別中的數值、字串、布林,也可以是字典、物件、列表等。django
提供了點號來訪問複雜資料結構。
- 列表、元組的元素可以使⽤索引引⽤,不能使⽤負索引,語法:變數.索引
- 字典: 字典變數.key
- 物件: 物件.屬性 物件.⽅法名(⽅法不能有引數)
當模板系統在變數名中遇到點時,按照以下順序嘗試進⾏查詢:
- 字典型別查詢
- 屬性查詢
- 方法呼叫
- 列表型別索引
如果模板中引⽤變數未傳值,則會被置為空,不會報錯,除⾮你對其進⾏了操作。
3.2 過濾器
過濾器是在變數顯示之前修改它的值的⼀個⽅法,過濾器使⽤管道符。過濾器可以
串聯呼叫
語法格式:
{{ 變數|方法 }}
常⻅的過濾器⽅法:
方法名 | 作用 | 示例 |
---|---|---|
default | 預設值 | {{ li|default:"預設值"}} |
default_if_none | 如果變數是none,則顯示預設值 | {{ value|default_if_none:'"hello" }} |
cut | 從字元中刪除指定字元 | {{ value|cut:" "}} 刪除value中所有的空格 |
length | 獲取字串或列表的⻓度 | {{ str1|length}} |
lower | 將所有字⺟都變為⼩寫 | {{ value|lower}} |
upper | 將所有字⺟都變為⼤寫 | {{ value|upper}} |
truncatechars | 擷取字串前n個字元 | {{ value|truncatechars:9 }} |
date | 格式化⽇期字串 | {{ value|date:"Y-m-d H:i:s"}} |
add | 增加變數的值 | {{ num|add:"3"}} |
divisibleby | 把變數的值除以指定值 | {{ value|divisibleby:"3"}} |
first | 獲取列表第⼀個元素 | {{ value|first }} |
last | 獲取列表最後⼀個元素 | {{ value|last }} |
join | 將列表內容連結為一個字串 | {{ value|join:'-' }} |
autoescape | 設定或取消轉義 | {% autoescape off %}{{ data }}{%endautoescape %} |
-
⾃定義過濾器
內建過濾器功能有限,如果不能滿足需求,可以自己定義過濾器。
-
在app⾥建立⼀個包:templatetags(名字必須是這個,且為包)
-
在包⾥建立⼀個py⽂件
from django import template # 例項化⾃定義過濾器註冊物件 register = template.Library() # name代表在模板中使⽤的過濾器的名稱 @register.filter(name='hello') def hello(value,arg): """ :param value: 傳給hello過濾的值 :param arg: hello⾃帶的引數 :return: """ return value + str(arg) @register.filter('time_ago') def time_ago(value): """ 定義⼀個距離當前時間多久之前的過濾器 :param value: :return: 1.如果時間間隔⼩於1分鐘內,那麼就顯示剛剛 2.如果時間間隔⼤於1分鐘⼩於1⼩時,那麼就顯示xx分鐘前 3.如果時間間隔⼤於1⼩時⼩於24⼩時,那麼就顯示xx⼩時前 4.如果時間間隔⼤於24⼩時⼩於30天,那麼就顯示xx天前 5.如果時間間隔⼤於30天,那麼就顯示具體時間 """ if not isinstance(value, datetime.datetime): return value now = datetime.datetime.now() timestamp = (now - value).total_seconds() if timestamp < 60: return '剛剛' elif timestamp >= 60 and timestamp < 60 * 60: return '{}分鐘前'.format(int(timestamp / 60)) elif timestamp >= 60 * 60 and timestamp < 60 * 60 * 24: return '{}⼩時前'.format(timestamp / 60 / 60) elif timestamp >= 60 * 60 * 24 and timestamp < 60 * 60 * 23 * 30: return‘{}天前'.format(int(timestamp/68/68/24)) else: return value.strftime('%Y-%m-%d %H:%M')
-
在模板中使⽤
{% load customfilter %} #載入⾃定義過濾器的模組 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ name |hello:' how are you' }} #使⽤⾃定義過濾器 </body> </html>
-
3.3 內建標籤
語法:{% tag %}
1.if標籤
{% if express1 %}
# to do
{% elif express2 %}
# to do
{% else %}
# to do
{% endif %}
- if表示式中使⽤以下運算子(優先順序從⾼到低):
- < >= <= == !=
- in 、not in
- is、is not
- not
- and
- or
- 不要在表示式中使⽤(),可以使⽤if巢狀實現功能
- 不⽀持 if 3 < b < 5這種寫法
2.for
遍歷可迭代物件
{% for x in y %}
...
{% endfor %}
- 反向迭代(reversed)
{% for value in c [1,2,3,4,5] reversed %}
<span>{{ value }}---</span>
{% endfor %}
- empty 當可迭代物件為空或不存在時執⾏,否則不執⾏
{% for value in c %}
<span>{{ value }}---</span>
{% empty %}
資料不存在
{% endfor %}
- 字典迭代
# e = {'a1':20,'b1':40}
{% for k,v in e.items %}
<div>{{ k }}---{{ v }}</div>
{% endfor %}
- 獲取for迴圈迭代的狀態
變數名稱 | 變數說明 |
---|---|
forloop.counter | 獲取迭代的索引 從1開始 |
forloop.counter0 | 獲取迭代的索引 從0開始 |
forloop.revcounter | 迭代的索引從最⼤遞減到1 |
forloop.revcounter0 | 迭代的索引從最⼤遞減到0 |
forloop.first | 是否為第⼀次迭代 |
forloop.last | 是否為最後⼀次迭代 |
forloop.parentloop | 獲取上層的迭代物件 |
{% for i in c %}
<li>{{ forloop.first }}</li>
<li>{{ forloop.last }}</li>
<li>{{ forloop.counter }}</li>
<li>{{ forloop.counter0 }}</li>
<li>{{ forloop.revcounter }}</li>
<li>{{ forloop.revcounter0 }}</li>
{% endfor %}
3. ifequal/ifnotequal
⽤於判斷兩個值相等或不等的
{% ifequal var var %}
{% endifequal %}
{% ifnotequal var var %}
{% endifnotequal %}
4.註釋
- 單行註釋
{# 註釋內容 #}
- 多行註釋
{% comment %}
...
{% endcomment %}
5.跨站請求偽造 csrf
防⽌⽹站受第三⽅伺服器的惡意攻擊(確定表單到底是不是本⽹站的表單傳遞過來
的)。csrf相當於在表達中增加了⼀個隱藏的input框,⽤於向伺服器提交⼀個唯⼀
的隨機字串⽤於伺服器驗證表單是否是本伺服器的表單。
使⽤:
settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
表單裡
<form action=
"" method="post">
{% csrf_token %}
<input type="text" name="username">
<p><input type="submit"></p>
</form>
- 全站禁用csrf
#在settings中設定
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
- 區域性禁⽤csrf
#在不想檢驗csrf的檢視函式前新增裝飾器@csrf_exempt。
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def csrf1(request):
pass
- ajax驗證csrf
Ajax提交資料時候,攜帶CSRF:
a. 放置在data中攜帶
<form method="POST" action="/csrf1.html">
{% csrf_token %}
<input id="username" type="text" name="username" />
<input type="submit" value="提交"/>
<a onclick="submitForm();">Ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function submitForm(){
var csrf = $('input[name="csrfmiddlewaretoken"]').val();
var user = $('#user').val();
$.ajax({
url: '/csrf1.html',
type: 'POST',
data: { "user":user,'csrfmiddlewaretoken': csrf},
success:function(arg){
console.log(arg);
}
})
}
</script>千
注意:
csrf的意義在於 給每⼀個表單都設定⼀個唯⼀的csrf的值 並且cookie也儲存⼀份
當提交表單過來的時候 判斷cookie中的值 和csrf_token中的值 是否都為本⽹站⽣
成的 如果驗證通過則提交 否則 403
6.模板導⼊標籤( include)
可以把指定html⽂件程式碼導⼊到當前⽂件,實現模板程式碼的復⽤/重⽤。語法格
式:
{% include '路徑/xxx.html' %}
7.url標籤
在模板中url標籤可⽤於反向解析
<h2><a href="{% url 'App:index' %}">動態⽣成路由地址不帶參的跳轉</a>
</h2>
<h2><a href="{% url 'App:args1' 1 2 %}">動態⽣成路由地址帶參的跳轉</a>
</h2>
<h2><a href="{% url 'App:args1' num1=1 num2=2 %}">動態⽣成路由地址帶關
鍵字引數的跳轉</a></h2>
四、模板繼承
在整個⽹站中,如何減少共⽤⻚⾯區域(⽐如站點導航)所引起的重複和冗餘代
碼?Django 解決此類問題的⾸選⽅法是使⽤⼀種優雅的策略—— 模板繼承 。
本質上來說,模板繼承就是先構造⼀個基礎框架模板,⽽後在其⼦模板中對它所包
含站點公⽤部分和定義塊進 ⾏過載。
- {% extends %} 繼承⽗模板
- {% block %} ⼦模板可以過載這部分內容。
- {{ block.super }}調⽤⽗模板的程式碼
使⽤繼承的⼀種常⻅⽅式是下⾯的三層法:
- 建立base.html模板,在其中定義站點的主要外觀感受。這些都是不常修改甚
⾄從不修改的部分。 - 為每種型別的⻚⾯建立獨⽴的模板,例如論壇⻚⾯或者圖⽚庫。這些模板拓展
相應的區域模板。 - ⾃⼰的⻚⾯繼承⾃模板,覆蓋⽗模板中指定block
注意事項:
- 如果在模板中使⽤ {% extends %} ,必須保證其為模板中的第⼀個模板標記。
否則,模板繼承將不起作 ⽤。 - ⼀般來說,基礎模板中的 {% block %} 標籤越多越好。
- 如果發覺⾃⼰在多個模板之間有重複程式碼,你應該考慮將該程式碼放置到⽗模板
的某個 {% block %} 中。 - 不在同⼀個模板中定義多個同名的 {% block %} 。
- 多數情況下, {% extends %} 的引數應該是字元,但是如果直到運⾏時⽅能確
定⽗模板名稱,這個引數也 可以是個變數。
五、靜態資源配置
什麼是靜態資源:css、js、images 需要從外部導⼊的資源
5.1建立static⽂件夾(通常放在根⽬錄下)
5.2需要在settings註冊
STATIC_URL='/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static')
]
5.3在模板中使⽤靜態資源
{% load static %} #放置到模板開頭
<img src="/static/img/img.jpeg" alt=""> #硬編碼
<img src="{% static 'img/img.jpeg' %}" alt=""> #動態寫法,建議⽤這種
六、jinja2模板引擎配置
- 安裝jinja2模板引擎
pip install jinja2
- 設定jinja2環境變數
# -*- coding: utf-8 -*-
from jinja2 import Environment
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
def jinja2_environment(**options):
env = Environment(**options)
env.globals.update({
'static': staticfiles_storage.url,
'url': reverse,
})
return env
-
配置(setting.py)
- 獨⽴使⽤jinja2,不使⽤Django模板引擎
#獨⽴使⽤jinja2 INSTALLED_APPS = [ #'django.contrib.admin', # 註釋了admin ..... ] #模板配置 TEMPLATES = [{ 'BACKEND': 'django.template.backends.jinja2.Jinja2',#jinja2模版 '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', ], 'environment': 'App.jinja2_env.environment', # 配置環 境,jinja的配置⽂件位置 }, }, ]
- 兩個同時使⽤
#模板配置 TEMPLATES = [{ 'BACKEND': 'django.template.backends.jinja2.Jinja2',#jinja2模 版 'DIRS': [ os.path.join(BASE_DIR, 'templates2'),#修改模版⽂件位置 ], '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', ], 'environment': 'App.jinja2_env.environment', # 配置環 境,jinja的配置⽂件位置 }, }, { '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', ], }, }, ]
-
靜態資源和url
<p>
<a href="{{ url('app:index') }}">dddd</a>
</p>
<img src="{{ static('images/1.jpg') }}" alt="">