者 | 單雨
責(zé)編 | 胡巍巍
出品 | CSDN(ID:CSDNnews)
前言
為了實(shí)現(xiàn)模板封裝和復(fù)用,提高HTML界面調(diào)試便捷性以及前后端解耦等目標(biāo),Django定義了自己的網(wǎng)絡(luò)模板語(yǔ)言。
當(dāng)前介紹模板語(yǔ)言的官方文檔已經(jīng)非常完備,幾乎涵蓋了開發(fā)中需要用到的知識(shí)點(diǎn)和需要注意的問(wèn)題,但同時(shí)官方文檔也存在一些問(wèn)題:
翻譯不夠完善,帶來(lái)閱讀的困難;
一些知識(shí)點(diǎn)的介紹過(guò)于簡(jiǎn)短,存在大量的頁(yè)內(nèi)鏈接,閱讀時(shí)需要跳轉(zhuǎn)到不同的頁(yè)面,閱讀不連貫。
本文基于官方文檔系統(tǒng)介紹了Django模板語(yǔ)言的基礎(chǔ)知識(shí)點(diǎn),方便快速了解Django模板語(yǔ)言。
模板系統(tǒng)設(shè)計(jì)哲學(xué)
Django的模板系統(tǒng)不是簡(jiǎn)單的把Python嵌入到HTML中。
它的設(shè)計(jì)宗旨是:模板系統(tǒng)旨在展示內(nèi)容, 而不是程序邏輯,因此不在HTML頁(yè)面中嵌入Python。
簡(jiǎn)單的說(shuō),模板只負(fù)責(zé)渲染數(shù)據(jù),大多數(shù)邏輯應(yīng)該交給視圖(view)進(jìn)行處理。
模板簡(jiǎn)介
模板是一個(gè)簡(jiǎn)單的文本文件。它可以生成任何基于文本的格式(如 HTML,XML,CSV等)。除了基本的HTML標(biāo)簽外,模板還包含兩種額外的元素——變量和標(biāo)簽。
模板中包含的變量可以被替換為變量的值,標(biāo)簽則被替換為相應(yīng)的模板控制邏輯。示例:
django
{% extends "base_generic.html" %}
{% block title %}{{ section.title }}{% endblock %}
{% block content %}
<h1>{{ section.title }}</h1>
{% for story in story_list %}
<h2>
<a href="{{ story.get_absolute_url }}">
{{ story.headline|upper }}
</a>
</h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}
`{{ section.title }}`在模板渲染時(shí)將會(huì)被變量的值替換,for標(biāo)簽可以實(shí)現(xiàn)模板的循環(huán)渲染。
基礎(chǔ)語(yǔ)法
變量
變量實(shí)現(xiàn)從模板上下文字典(返回HTTP響應(yīng)時(shí)傳遞過(guò)來(lái)的字典)中輸出一個(gè)值,這是一個(gè)類似于dict的對(duì)象,包含鍵值對(duì)。當(dāng)模板引擎遇到一個(gè)變量時(shí),它會(huì)計(jì)算該變量,并用結(jié)果替換它。
變量名由字母、數(shù)字字符和下劃線("_")組成,但不能以下劃線開頭。點(diǎn)(".")也出現(xiàn)在變量中,代表屬性調(diào)用,變量名中不能有空格或標(biāo)點(diǎn)符號(hào)。
示例:
django
My first name is {{ first_name }}. My last name is {{ last_name }}.
當(dāng)傳入一個(gè)上下文字典`{'first_name': 'John', 'last_name': 'Doe'}`時(shí),將會(huì)渲染得到:
django
My first name is John. My last name is Doe.
模板中的變量被字典中的值替換了。
變量還可以使用點(diǎn)表示法實(shí)現(xiàn)字典查找、屬性查找和列表索引查找等操作:
django
{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}
點(diǎn)表示法底層原理
當(dāng)模板系統(tǒng)遇到一個(gè)點(diǎn),它會(huì)按順序嘗試下面的動(dòng)作:
1. 字典查詢
2. 屬性或方法查找
3. 數(shù)字索引查詢
如果結(jié)果值是可調(diào)用的,則調(diào)用該值時(shí)將不帶參數(shù),調(diào)用的結(jié)果成為新的模板值。
當(dāng)進(jìn)行能覆蓋字典查找的操作時(shí),這種查找順序可能會(huì)造成一些意想不到的行為。例如:如果試圖循環(huán)一個(gè)collection .defaultdict字典對(duì)象:
django
{% for k, v in defaultdict.items %}
{其他操作}
{% endfor %}
因?yàn)樽值洳檎沂鞘紫劝l(fā)生的,所以這個(gè)行為會(huì)先提供一個(gè)默認(rèn)值,而不是使用預(yù)期的.items方法。在這種情況下,應(yīng)該首先考慮使用字典查找,而不是使用字典的屬性調(diào)用。
注意
屬性通常被解釋為一個(gè)文本字符串,防止和同名的變量沖突。例如{{foo.bar}}中的屬性“bar”將被解釋為一個(gè)文本字符串,如果模板上下文中存在變量“bar”,則不會(huì)使用該變量的值。
以下劃線開頭的變量屬性可能不能訪問(wèn),因?yàn)樗鼈兺ǔ1徽J(rèn)為是私有的。
如果引用不存在的變量,模板系統(tǒng)將插入string_if_invalid選項(xiàng)的值,該選項(xiàng)默認(rèn)設(shè)置為“”(空字符串)。
標(biāo)簽
標(biāo)簽在模板渲染過(guò)程中提供任意邏輯。標(biāo)簽可以輸出內(nèi)容,作為控制結(jié)構(gòu),例如“if”語(yǔ)句或“for”循環(huán),從數(shù)據(jù)庫(kù)獲取內(nèi)容,甚至允許訪問(wèn)其他模板標(biāo)簽。
(1)標(biāo)簽聲明
標(biāo)簽的一般形式為:
django
{% tag %}
示例:
django
{% csrf_token %}
(2)傳入?yún)?shù)
django
{% cycle 'odd' 'even' %}
(3)成對(duì)使用的標(biāo)簽
有些標(biāo)簽需要開始和結(jié)束標(biāo)簽:
django
{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}
(4)常用標(biāo)簽
for:循環(huán)數(shù)組中的每個(gè)元素. 比如, 顯示列表 `athlete_list` 中每個(gè)元素的 `name` 屬性。
django
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
if 、elif和else:在上面,如果athlete_list不為空,則{{athlete_list|length}}變量將顯示運(yùn)動(dòng)員的數(shù)量。
否則,如果athlete_in_locker_room_list不為空,則會(huì)顯示“Athletes should be out…”消息。如果兩個(gè)列表都為空,則顯示“No athletes”。
也可以在if標(biāo)簽里使用過(guò)濾器和各種操作符:
django
{% if athlete_list|length > 1 %}
Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
Athlete: {{ athlete_list.0.name }}
{% endif %}
注意
雖然上面的示例可以工作,但是要注意,大多數(shù)模板過(guò)濾器都返回字符串,因此使用過(guò)濾器進(jìn)行數(shù)學(xué)比較通常不會(huì)正常工作,而長(zhǎng)度是個(gè)例外。
(5)更多
Django有很多內(nèi)置標(biāo)簽,更多關(guān)于內(nèi)置標(biāo)簽的信息請(qǐng)參考官方文檔:
https://docs.djangoproject.com/zh-hans/2.2/ref/templates/builtins/ref-templates-builtins-tags
如果需要編寫自定義標(biāo)簽,請(qǐng)參考官方文檔
https://docs.djangoproject.com/zh-hans/2.2/howto/custom-template-tags/howto-writing-custom-template-tags
如果需要對(duì)使用的標(biāo)簽和自定義的標(biāo)簽做一份說(shuō)明文檔,可以使用Django提供的文檔工具,詳情請(qǐng)參考:
https://docs.djangoproject.com/zh-hans/2.2/ref/contrib/admin/admindocs/
過(guò)濾器
簡(jiǎn)介
過(guò)濾器可以對(duì)變量做一些操作,例如給變量賦值,改變變量的值等。
修改變量顯示
過(guò)濾器可以修改變量的顯示。例如:
django
{{ name|lower }}
通過(guò)過(guò)濾器lower變量{{ name }}變?yōu)榱诵懽址ㄟ^(guò)管道符(|)間隔變量和過(guò)濾器來(lái)使用過(guò)濾器。
鏈?zhǔn)秸{(diào)用過(guò)濾器
一個(gè)過(guò)濾器的輸出可以作為下一個(gè)過(guò)濾器的輸入。
{{ text|escape|linebreaks }}是一種常用的轉(zhuǎn)換方式, 在這之后換行符被替換為了 <p> 標(biāo)簽。
轉(zhuǎn)換變量和標(biāo)簽參數(shù)
過(guò)濾器轉(zhuǎn)換變量和標(biāo)簽參數(shù)的值。示例:
django
{{ django|title }}
傳入`{'django': 'the web framework for perfecalist With deadline '}`上下文字典時(shí),該模板呈現(xiàn)為:
django
The Web Framework For Perfectionists With Deadlines
傳入?yún)?shù)給過(guò)濾器
示例1:
django
{{ my_date|date:"Y-m-d" }}
my_date將會(huì)被替換為當(dāng)前日期。
示例2:
django
{{ bio|truncatewords:30 }}
將會(huì)會(huì)顯示 `bio` 變量的前30個(gè)字符
注意
過(guò)濾器參數(shù)中如果包含空格和標(biāo)點(diǎn)符號(hào),必須使用引號(hào)“”括起來(lái),例如,要用逗號(hào)和空格連接列表,可以使用{{list|join:", "}}。
Django提供了大約60個(gè)內(nèi)置模板過(guò)濾器,請(qǐng)參考官方文檔:
https://docs.djangoproject.com/zh-hans/2.2/ref/templates/builtins/ref-templates-builtins-filters,
下面列舉一些常用的過(guò)濾器:
default
如果變量為false或空,則使用給定的默認(rèn)值。否則,使用變量的值。例如:
django
{{ value|default:"nothing" }}
如果 `value` 沒(méi)有提供或者為空,那么將它顯示為 "`nothing`" 。
length
返回值的長(zhǎng)度。這對(duì)字符串和列表都適用。例如:
django
{{ value|length }}
如果 `value` 為 `['a', 'b', 'c', 'd']`, 那么他將被顯示為 `4`。
filesizeformat
將值格式化為“人類可讀的”文件大小(即“13kb”、“4.1 MB”、“102字節(jié)”等)。例如:
django
{{ value|filesizeformat }}
如果值為123456789,則輸出為117.7 MB。
如果需要自定義過(guò)濾器,請(qǐng)參考請(qǐng)官方文檔:
https://docs.djangoproject.com/zh-hans/2.2/howto/custom-template-tags/
注釋
示例:
單行注釋
django
{ this won't be rendered }
多行注釋:{% comment %} 和{% endcomment %}
django
<p>Rendered text with {{ pub_date|date:"c" }}</p>
{% comment "Optional note" %}
<p>Commented out text with {{ create_date|date:"c" }}</p>
{% endcomment %}
注意:Comment標(biāo)簽不能嵌套使用。
作者簡(jiǎn)介:?jiǎn)斡辏?0后工科男,偽文藝青年。目前就讀于北京理工大學(xué)宇航系,喜歡研究AI,網(wǎng)絡(luò)爬蟲,微信小程序以及機(jī)器人,癡迷于Coding,睡前必?cái)]碼。
【END】
前話
Hello,小伙伴們大家新年好,本篇是今年第一篇,也籌劃許久,本篇主題為美食,系html5網(wǎng)站模板,div加css布局,網(wǎng)頁(yè)資源分開存儲(chǔ)以便管理,網(wǎng)頁(yè)結(jié)構(gòu)清晰簡(jiǎn)單,希望本篇能夠助力各位萌新
主題
《周末の食記》
美食能撫平一切的憂傷
簡(jiǎn)介
文件結(jié)構(gòu)包含了css、fonts、images、js和html,運(yùn)用html5技術(shù),包括nav標(biāo)簽、header標(biāo)簽和footer標(biāo)簽等,采用bootstrap進(jìn)行布局
圖摘
目錄
編碼
<div class="ftco-46-row d-flex flex-column flex-lg-row">
<div class="ftco-46-text ftco-46-arrow-right">
<h4 class="ftco-46-subheading">Food</h4>
<h3 class="ftco-46-heading">揚(yáng)州炒飯</h3>
<p class="mb-5">一碗不一樣的炒飯,讓你難以拒絕.</p>
<p><a href="#" class="btn-link">更多 <span class="ion-android-arrow-forward"></span></a></p>
</div>
<div class="ftco-46-image" style="background-image: url(images/img_3.jpg);"></div>
<div class="ftco-46-text ftco-46-arrow-up">
<h4 class="ftco-46-subheading">Food</h4>
<h3 class="ftco-46-heading">藍(lán)莓酸奶冰激凌</h3>
<p class="mb-5">觸動(dòng)您的心靈,令人甜蜜至極,難以忘懷,心曠神怡的味覺(jué)享受,精選一級(jí)的夏威夷果仁,入口絲滑</p>
<p><a href="#" class="btn-link">更多 <span class="ion-android-arrow-forward"></span></a></p>
</div>
</div>
結(jié)語(yǔ)
如果人的一生總的能量是固定的話,那就節(jié)省開支,延長(zhǎng)時(shí)間,喜怒哀樂(lè)不溢于言表,不困于心智,保持樂(lè)觀心態(tài)
源于公眾號(hào):Python野路子
我們之前學(xué)習(xí)的,都是在視圖函數(shù)直接返回文本,在實(shí)際中我們更多的是帶有樣式的HTML代碼,這樣可以讓瀏覽器渲染出非常漂亮的頁(yè)面,目前市面上有非常多的模板系統(tǒng),其中最常用的是DTL和Jinja2,DTL(Django Template Language),也就是Django自帶的模板語(yǔ)言,當(dāng)然也可以配置Django支持Jinja2,但是作為Django內(nèi)置的模板語(yǔ)言,不會(huì)產(chǎn)生一些不兼容的情況,最好還是使用內(nèi)置的。
DTL模板是一種帶有特殊語(yǔ)法的HTML文件,這個(gè)HTML文件可以被Django編譯,可以傳遞參數(shù)進(jìn)去,實(shí)現(xiàn)數(shù)據(jù)動(dòng)態(tài)化,在編譯完成后,生成一個(gè)普通的HTML文件,然后發(fā)送給客戶端。
可以使用render函數(shù)進(jìn)行渲染,將數(shù)據(jù)渲染到指定的HTML模板中。
我們?cè)谥暗幕A(chǔ)上,再新建一個(gè)article的app,除了剛開始第一個(gè)應(yīng)用,我們?cè)?span style="color: #1E6BB8; --tt-darkmode-color: #1E6BB8;">pycharm創(chuàng)建項(xiàng)目的時(shí)候,填了app名字,pycharm會(huì)自動(dòng)幫我們創(chuàng)建應(yīng)用,后續(xù)如果還要再新建應(yīng)用,我們都要使用命令方式才能新建app。
應(yīng)用創(chuàng)建完成了,接下來(lái)我們需要在配置文件settings.py里面注冊(cè)應(yīng)用app。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user.apps.UserConfig', # 使用pycharm創(chuàng)建的應(yīng)用,pycharm會(huì)自動(dòng)注冊(cè)
'article' # 手工命令方式創(chuàng)建的應(yīng)用app,需要手工在這注冊(cè)應(yīng)用!
]
路由和視圖代碼:
# 主urls.py
path('article/', include('article.urls', namespace='article'))
# 應(yīng)用urls.py,創(chuàng)建的應(yīng)用這個(gè)文件不會(huì)自動(dòng)生成,需要我們新建
from django.urls import path
from . import views
app_name = 'article'
urlpatterns = [
path('index/', views.index, name='index')
]
# 視圖views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html') # 使用render渲染模板
模板文件,項(xiàng)目根目錄下templates目錄(若沒(méi)有自動(dòng)生成,則手工新建此文件夾)下新建index.html模板文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章首頁(yè)</title>
</head>
<body>
<h2>文章首頁(yè)</h2>
</body>
</html>
查看效果:
使用render函數(shù)將模板成功渲染出來(lái)了。
在項(xiàng)目的settings.py配置文件中,有一個(gè)TEMPLATES配置。
TEMPLATES = [
{
# 模板引擎,就是Django內(nèi)置的模板引擎,
# 若要配置為Jinja2:django.template.backends.jinja2.Jinja2
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# BASE_DIR,項(xiàng)目根目錄路徑下的templates查找
#'DIRS': [os.path.join(BASE_DIR, 'templates')] # Django2中使用
'DIRS': [BASE_DIR / 'templates'], # Django3中使用
,
# 是否從app應(yīng)用下的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',
],
},
},
]
這個(gè)配置包含了模板引擎的配置,這個(gè)配置包含了模板引擎的配置,模板查找路徑的配置,模板上下文的配置等,模板路徑可以2個(gè)地方配置。
我們想要將后臺(tái)數(shù)據(jù)渲染到頁(yè)面上,這就需要用到模板變量。
# 主urls.py
path('article/', include('article.urls', namespace='article'))
# 應(yīng)用urls.py
path('index/', views.index, name='index')
# 視圖views.py
from django.shortcuts import render
class Article:
def __init__(self, title):
self.title = title
def index(request):
context = {
'msg': '模板變量', # 字符串,
'article': Article('Django框架之模板文件'), # 實(shí)例一個(gè)對(duì)象,
'author': {'name': 'admin'}, # 一個(gè)字典
'tag': ['python入門', 'python進(jìn)階', '數(shù)據(jù)庫(kù)'] # 列表或元組
}
return render(request, 'index.html', context=context) # 傳入一個(gè)字典
模板文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章首頁(yè)</title>
</head>
<body>
<h2>文章首頁(yè)</h2>
<p>{{ msg }}</p> <!-- 在模板中使用變量,需要將變量放到 {{ 變量 }} 中 -->
<h3>{{ article.title }}</h3> <!-- 要訪問(wèn)對(duì)象的屬性,通過(guò)"對(duì)象.屬性名" 訪問(wèn) -->
<p>作者:{{ author.name }}</p> <!-- 要訪問(wèn)字典key對(duì)應(yīng)值value,通過(guò)"字典.key" 訪問(wèn),不能[]形式 -->
<ul>
<li>{{ tag.0 }}</li> <!-- 要訪問(wèn)列表或者元組的元素,通過(guò)"列表.索引" 訪問(wèn) -->
<li>{{ tag.1 }}</li>
<li>{{ tag.2 }}</li>
</ul>
</body>
</html>
效果:
在模板中使用變量:語(yǔ)法:{{變量名}} 1)命名由字母和數(shù)字以及下劃線組成,不能有空格和標(biāo)點(diǎn)符號(hào)。2)不要和python或django關(guān)鍵字重名。原因:如果data是一個(gè)字典,那么訪問(wèn)data.items將會(huì)訪問(wèn)data這個(gè)字典的key名為items的值,而不會(huì)訪問(wèn)字典的items方法。
標(biāo)簽語(yǔ)法:{% 標(biāo)簽名稱 %} {% 結(jié)束標(biāo)簽名稱 %}。
例:{%tag%} {%endtag%}
if標(biāo)簽相當(dāng)于python中的if語(yǔ)句,有elif和else相對(duì)應(yīng),可以使用and、or、in、not、==、!=、<=、>=、is、is not,來(lái)進(jìn)行判斷。
視圖views.py:
def index(request):
age = random.randint(6, 20) # 隨機(jī)生成6-20的整數(shù)
context = {
'age': age
}
return render(request, 'index.html', context=context)
模板文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章首頁(yè)</title>
</head>
<body>
<p>
<span>年齡:<strong>{{ age }}</strong>,</span>
<span>適合讀</span>
<span>
{% if age >= 18 %}
【大學(xué)】書籍
{% elif 12 < age and age < 18 %}
【高中】書籍
{% else %}
【小學(xué)】書籍
{% endif %}
</span>
</p>
</body>
</html>
再刷新一次:
我們還可以使用ifequal/ifnotequal來(lái)比較兩個(gè)值是否相等,如:
{% ifequal price 0 %}
<span class="publicimg"><img src="/images/public.png"></span>
{% else %}
<span class="publicimg"><img src="/images/vip.png"></span>
{% endifequal %}
for標(biāo)簽相當(dāng)于python中的for循環(huán),可以遍歷列表、元組、字符串、字典等一切可以遍歷的對(duì)象。
# 視圖views.py
def index(request):
context = {
'articles': [ # 列表
'21天學(xué)會(huì)Python',
'C++從入門到入土',
'Mysql從入門到跑路'
],
'author': { # 字典
'name': '老P',
'age': 18,
'sex': 'boy'
},
'artile_details': [
{'title': '21天學(xué)會(huì)Python', 'author': '小P', 'words': 1000},
{'title': 'C++從入門到入土', 'author': '中P', 'words': 2000},
{'title': 'Mysql從入門到跑路', 'author': '老P', 'words': 3000}
]
}
return render(request, 'index.html', context=context)
模板文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章首頁(yè)</title>
</head>
<body>
<ul>
{% for article in articles %}
<li>{{ article }}</li>
{% endfor %}
</ul>
<p>獲取字典鍵:</p>
<ul>
{% for key in author.keys %}
<li>{{ key }}</li>
{% endfor %}
</ul>
<p>獲取字典值:</p>
<ul>
{% for value in author.values %}
<li>{{ value }}</li>
{% endfor %}
</ul>
<p>獲取字典鍵值對(duì):</p>
<ul>
{% for key, value in author.items %}
<li>{{ key }}:{{ value }}</li>
{% endfor %}
</ul>
<p>文章信息列表:</p>
<table border="1">
<thead>
<tr>
<th>序號(hào)</th>
<th>標(biāo)題</th>
<th>作者</th>
<th>字?jǐn)?shù)</th>
</tr>
</thead>
<tbody>
{% for article_detail in artile_details %}
{% if forloop.first %} <!-- 是否第一次遍歷 -->
<tr style="background: red;">
{% elif forloop.last %} <!-- 是否最后一次遍歷 -->
<tr style="background: pink;">
{% else %}
<tr>
{% endif %}
<td>{{ forloop.counter }}</td> <!-- 當(dāng)前迭代的次數(shù),下標(biāo)從1開始。-->
<td>{{ article_detail.title }}</td>
<td>{{ article_detail.author }}</td>
<td>{{ article_detail.words }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
<!--
forloop.counter:當(dāng)前迭代的次數(shù),下標(biāo)從1開始。
forloop.counter0:當(dāng)前迭代的次數(shù),下標(biāo)從0開始。
forloop.first:返回bool類型,如果是第一次迭代,返回true,否則返回false。
forloop.last:返回bool類型,如果是最后一次迭代,返回True,否則返回False。
forloop.revcounter 反向循環(huán)位置(列表的最后一位是l ,列表第一位是n )
forloop.revcounter0 反向循環(huán)位置(列表的最后一位是0 , 列表第一位是n- 1 )
-->
還有個(gè)for...in...empty,在遍歷的時(shí)候如果需要遍歷的對(duì)象沒(méi)有為空,則執(zhí)行empty下的操作。
{% for comment in comments %}
{{ comment }}
{# 在for循環(huán)之前檢查列表大小是常見(jiàn)的,當(dāng)列表為空的時(shí)候給出特別提示,這是常見(jiàn)的,所以for支持可選empry來(lái)當(dāng)為空時(shí)輸出 #}
{% empty %} <!-- comments沒(méi)有或?yàn)榭眨瑒t執(zhí)行empty下的。-->
還沒(méi)有評(píng)論~~~
{% endfor %}
有時(shí)候可能在頁(yè)面上要用上循環(huán)幾次,可以用:
{% for item in 'x'|ljust:'4' %} 循環(huán)四次
{%endfor %}
使用一個(gè)簡(jiǎn)單地名字緩存一個(gè)復(fù)雜的變量,當(dāng)你需要使用一個(gè)代價(jià)較大的方法(比如訪問(wèn)數(shù)據(jù)庫(kù))很多次的時(shí)候這是非常有用的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章首頁(yè)</title>
</head>
<body>
{{ title }}
{% with t1=title %} <!-- 使用= -->
{{ t1 }}
{% endwith %}
{% with title as t2%} <!-- 使用as 取別名 -->
{{ t2 }}
{% endwith %}
</body>
</html>
注意:定義的變量只能在with語(yǔ)句塊中使用,在with語(yǔ)句塊外面使用取不到這個(gè)變量。
在模板中,經(jīng)常要寫一些超鏈接,比如a標(biāo)簽中需要定義href屬性,當(dāng)然如果通過(guò)硬編碼的方式直接將這個(gè)url寫死也是可以,但是不便于以后維護(hù),比如需要修改url地址,那涉及到的地方都要修改,因此建議使用這種反轉(zhuǎn)的方式來(lái)實(shí)現(xiàn),類似于django中的reverse一樣。
# 應(yīng)用urls.py
from django.urls import path
from . import views
app_name = 'article'
urlpatterns = [
path('index/', views.index, name='index'),
path('book/', views.book, name='book'),
path('movie/', views.movie, name='movie'),
path('city/', views.city, name='city'),
path('book/hot/<int:book_id>', views.hot_book, name='hot_book')
]
# 視圖views.py
from django.shortcuts import render, HttpResponse
def index(request):
return render(request, 'index.html')
def book(request):
return HttpResponse('讀書頁(yè)面')
def movie(request):
return HttpResponse('電影頁(yè)面')
def city(request):
return HttpResponse('同城頁(yè)面')
def hot_book(request, book_id):
return HttpResponse('最熱門文章:%d'%book_id)
模板文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章首頁(yè)</title>
<style>
li{
list-style: none;
display: inline-block;
margin-right: 20px ;
}
</style>
</head>
<body>
<ul>
<li><a href="">首頁(yè)</a></li>
<li><a href="/article/book/">讀書</a></li> <!-- url硬編碼的方式 -->
<li><a href="{% url 'article:movie' %}">電影</a></li> <!-- url標(biāo)簽 -->
<li><a href="{% url 'article:city' %}">同城</a></li>
<li><a href="{% url 'article:hot_book' book_id=1 %}">最火文章</a></li> <!-- url標(biāo)簽 傳遞參數(shù),這個(gè)參數(shù)需要于url映射的參數(shù)名一樣,如果傳遞多個(gè)參數(shù),則空格隔開 -->
</ul>
</body>
</html>
DTL中默認(rèn)已經(jīng)開啟了自動(dòng)轉(zhuǎn)義,會(huì)將哪些特殊字符進(jìn)行轉(zhuǎn)義,比如會(huì)將<轉(zhuǎn)義成<等。
def index(request):
comment_info = '個(gè)人博客網(wǎng)站:<a href="http://www.qmpython.com"> 全民python</a>'
context = {
'info': comment_info
}
return render(request, 'index.html', context=context)
模板文件:
<p>個(gè)人博客網(wǎng)站:<a href="http://www.qmpython.com"> 全民python</a></p>
<p>
{{ info }} <!-- DTL默認(rèn)on開啟了自動(dòng)轉(zhuǎn)義,轉(zhuǎn)換成普通字符串 -->
</p>
<p>
{% autoescape off %} <!-- off關(guān)閉自動(dòng)轉(zhuǎn)義,解析html標(biāo)簽 -->
{{ info }}
{% endautoescape %}
</p>
如果你不知道自己在干什么,最好是使用自動(dòng)轉(zhuǎn)義,這樣網(wǎng)站才不容易出現(xiàn)XSS漏洞(比如有些網(wǎng)站進(jìn)行評(píng)論的時(shí)候,發(fā)一些含惡意的js代碼,如果不進(jìn)行自動(dòng)轉(zhuǎn)義會(huì)進(jìn)行渲染,而不會(huì)做成普通的字符串),比如我個(gè)人博客的評(píng)論就處理了。如果是可信用的,可以關(guān)閉自動(dòng)轉(zhuǎn)義,比如我的文章內(nèi)容是只允許我個(gè)人發(fā)表,是可信任的,所以我直接關(guān)閉。后面可以通過(guò)過(guò)濾器safe類似處理。
默認(rèn)在DTL模板中是會(huì)去解析那些特殊字符的,比如{% %}和{{ }}等,如果我們?cè)谀硞€(gè)代碼片段不想使用DTL的解析,那么我們可以將這段代碼放在verbatim標(biāo)簽中。
# 視圖
def index(request):
return render(request, 'index.html')
# 模板
<body>
{% if msg %}
{{ msg }}
{% else %}
沒(méi)消息
{% endif %}
<br>
{% verbatim %}
{% if msg %}
{{ msg }}
{% endif %}
{% endverbatim %}
</body>
這個(gè)主要是用在,有些前端模板語(yǔ)法layui還有vue中也使用{{}}來(lái)表示變量,這些與DTL類似,這樣就容易引起混淆,我們可以使用這個(gè)標(biāo)簽,來(lái)屏蔽掉DTL的解析,而讓前端模板去解析。
我們?cè)贖TML中經(jīng)常使用<!-- 注釋內(nèi)容 -->來(lái)進(jìn)行注釋,在Django模板中,我們還可以使用其他注釋。
{# 被注釋的內(nèi)容 #}:將中間的內(nèi)容注釋掉。只能單行注釋。
{% comment %}被注釋的內(nèi)容{% endcomment %}:可以多行注釋。
這種注釋,當(dāng)我們查看網(wǎng)頁(yè)元素的時(shí)候是看不到的,即沒(méi)有被渲染,而<!-- -->還是可以看到原樣字符串的。
1)首先,需要添加一個(gè)templatetags的文件夾, 自定義標(biāo)簽必須處在已經(jīng)安裝了的app(INSTALLED_APPS注冊(cè)了)中的一個(gè)名叫templatetags的包中。
templatetags 文件夾名字不能修改,這是django規(guī)定的。
myproject
|——myproject
|——__init__.py
|——asgi.py
|——settings.py
|——urls.py
|——wsgi.py
|——templates
|——myapp
|——migrations
|——templatetags
|——__init__.py
|——custom_tags.py
|——manage.py
2)在templatetags下添加一個(gè)python文件,如我這里創(chuàng)建一個(gè)custom_tags.py文件,在文件中添加對(duì)應(yīng)的自定義標(biāo)簽。
from django import template
# register的名字是固定的,不可改變
register = template.Library()
#使用裝飾器注冊(cè)自定義標(biāo)簽
@register.simple_tag
def curr_date(args):#args可傳參數(shù),根據(jù)實(shí)際需求而定
return datetime.datetime.now().strftime(args)
3)在要使用自定義標(biāo)簽的HTML模板中導(dǎo)入和使用自定義標(biāo)簽文件:
{# 加載自定義標(biāo)簽所在的文件名,由于templatetags的文件名是固定的,django可以直接找到過(guò)自定義標(biāo)簽文件所在的位置 #}
{% load custom_tags %}
<h3>自定義標(biāo)簽</h3>
時(shí)間日期:{% curr_date "%Y-%m-%d"%}
我們?cè)賮?lái)定義簡(jiǎn)單標(biāo)簽article/templatetags/menu_tags.py:
from django import template
# register的名字是固定的,不可改變
register = template.Library()
@register.simple_tag
def my_menu():
menu_list = ['首頁(yè)','項(xiàng)目實(shí)戰(zhàn)','每日一練']
return menu_list
模板文件templates/article/my_menu.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定義簡(jiǎn)單標(biāo)簽</title>
</head>
<body>
<h1>-----------菜單欄--------------</h1>
<br/>
{% load menu_tags %} <!-- 導(dǎo)入自定義的標(biāo)簽文件 -->
{% my_menu as menus %} <!-- 注意:這里需要使用as將標(biāo)簽文件返回的值賦給一個(gè)變量,然后再引用!!!-->
<ul>
{% for menu in menus %} {# 企圖使用 {% for menu in my_menu %} 是沒(méi)效果的 #}
<li>{{menu}}</li>
{% endfor %}
</ul>
</body>
</html>
視圖渲染views.py:
def test(request):
return render(request, 'article/my_menu.html')
一種比較普遍的tag類型只是渲染其他模塊顯示下的內(nèi)容,這樣的類型叫做inclusion_tag,常用的模板標(biāo)簽是通過(guò)渲染其他模板顯示數(shù)據(jù)的。
inclusion_tag作用:創(chuàng)建一個(gè)動(dòng)態(tài)頁(yè)面文件a.html,這個(gè)頁(yè)面可以在另外一個(gè)頁(yè)面b.html中被調(diào)用,實(shí)現(xiàn)這個(gè)頁(yè)面a中有的功能。
例如,上面一個(gè)導(dǎo)航頁(yè)面my_menu.html,基本在每個(gè)頁(yè)面中都要包含導(dǎo)航,如我的個(gè)人博客一樣:
那要怎么樣呢?
例如,我們?cè)谑醉?yè)模板中,先試下用include來(lái)引入導(dǎo)航模板看看(include這個(gè)后面具體學(xué)習(xí),這里只演示作用)。
與上面自定義簡(jiǎn)單標(biāo)簽類似,在自定義標(biāo)簽文件里面自定義包含標(biāo)簽:
from django import template
from article.models import Column
# register的名字是固定的,不可改變
register = template.Library()
#使用裝飾器注冊(cè)自定義包含標(biāo)簽
@register.inclusion_tag('article/my_menu.html') #將返回值傳給my_menu.html去,可以定義name,否則使用函數(shù)名
def my_menu():
# menus = ['首頁(yè)','項(xiàng)目實(shí)戰(zhàn)','每日一練']
menus = Column.objects.all()
return {'menus':menus}
my_menu.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定義包含標(biāo)簽</title>
</head>
<body>
<ul>
{% for menu in menus %}
<li> {{menu}} </li>
{% endfor %}
</ul>
</body>
</html>
調(diào)用的html頁(yè)面文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首頁(yè)</title>
</head>
<body>
{% load menu_tags %} <!-- 必須要先加載創(chuàng)建標(biāo)簽的文件-->
{% my_menu %} <!-- 調(diào)用my_menu.html頁(yè)面文件,這里使用該標(biāo)簽的函數(shù)名來(lái)調(diào)用-->
<h1>首頁(yè)</h1>
</body>
</html>
視圖渲染:
def test(request):
return render(request, 'article/index.html')
除了上述功能性標(biāo)簽外, Django 還提供了很多輔助性標(biāo)簽,這些標(biāo)簽只是為了使變量輸 出變得更加可讀,下面對(duì)這些標(biāo)簽進(jìn)行簡(jiǎn)單介紹。首先為了使用這些標(biāo)簽,需要在INSTALLED_APPS 中注冊(cè)django .contrib.humanize, 然后在模板中引用humanize:{% raw %}{% load humanize % }{% endraw %}
將數(shù)字1 ~ 9 轉(zhuǎn)換為英文單詞,但是其他數(shù)字不轉(zhuǎn)換,如數(shù)字10 將被原樣輸出。示例:
數(shù)字1 被轉(zhuǎn)換為one ;
數(shù)字2 被轉(zhuǎn)換為two ;
數(shù)字10 仍顯示10 ;
如果當(dāng)前工程語(yǔ)言是中文的話,數(shù)字將會(huì)被轉(zhuǎn)換為對(duì)應(yīng)的漢字,例如:
{% raw %}
{{ 1|apnumber }}
{{ 2|apnumber }}
{{ 5|apnurnber }}
{% endraw %}
如果當(dāng)前工程語(yǔ)言是中文的話,數(shù)字將會(huì)被轉(zhuǎn)換為對(duì)應(yīng)的漢字,例如:
輸出:
一
二
五
輸出以逗號(hào)分隔的數(shù)字,如4500 輸出4,500, 4500.2 輸出4,500.2 。
以文字形式輸出數(shù)字,如1000000 輸出“ 1.0 million ”, 1200000 輸出“ 1,2 Million ” 。對(duì)于中文系統(tǒng),將會(huì)輸出對(duì)應(yīng)的中文,如1200000 輸出" 1.2 百萬(wàn)” 。
將當(dāng)前日期以及前后一天輸出為today 、yesterday 和tomorrow ,而中文系統(tǒng)分別輸出 “今天”“昨天”和“明天” 。
對(duì)于日期時(shí)間格式,時(shí)間值與系統(tǒng)當(dāng)前時(shí)間比較,然后輸出結(jié)果。如當(dāng)前時(shí)間輸出 “ now ”, 29 秒前輸出“ 29 sec onds ago ” 。如果使用naturaltime 輸出今天、昨天、明天的話, 就會(huì)變成“現(xiàn)在”“ 23 小時(shí)·以后”“ 1 日之前” 。
將數(shù)字轉(zhuǎn)換為序數(shù),如l 輸出“ 1 st ”;2 輸出“ 2nd ”;3 輸出“ 3rd ” 。注意此時(shí)中文與 英文的輸出一樣。
在模板中,有時(shí)候需要對(duì)一些數(shù)據(jù)進(jìn)行處理以后才能使用,一般在Python中我們是通過(guò)函數(shù)的形式來(lái)完成的,因?yàn)樵?span style="color: #1E6BB8; --tt-darkmode-color: #1E6BB8;">DTL中,不支持函數(shù)的調(diào)用形式(),因此在模板中,則是通過(guò)過(guò)濾器來(lái)實(shí)現(xiàn)的,過(guò)濾器使用的是|來(lái)使用。語(yǔ)法:
{{ 變量名|過(guò)濾器名:傳給過(guò)濾器的參數(shù)}}
比如使用date過(guò)濾器。
# 視圖
from datetime import datetime
def index(request):
now_time = datetime.now()
context = {
'now_time': now_time
}
return render(request, 'index.html', context=context)
# 模板文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章首頁(yè)</title>
</head>
<body>
{{ now_time }} <!-- 原始渲染 -->
<br>
{{ now_time | date:'Y-m-d H:i:s' }} <!-- 通過(guò)過(guò)濾器,先將日期格式化,再渲染出-->
</body>
</html>
date時(shí)間過(guò)濾器格式:
格式字符描述示例Y4位數(shù)的年1999y2位數(shù)的年99m2位數(shù)的月01,09n1位數(shù)的月1,9,12d2位數(shù)的日01,09,31j1位數(shù)的日1,9,31g12小時(shí)制的一位數(shù)的小時(shí)1,9,12G24小時(shí)制的一位數(shù)小時(shí)0,8,23h12小時(shí)制的兩位數(shù)的小時(shí)01,09,12H24小時(shí)制的兩位數(shù)的小時(shí)01,13,24i分鐘00-59s秒00-59
django常用過(guò)濾器:
add:字符串相加,數(shù)字相加,列表相加,如果失敗,將會(huì)返回一個(gè)空字符串。default:提供一個(gè)默認(rèn)值,在這個(gè)值被django認(rèn)為是False的時(shí)候使用。比如:空字符串、None、[]、{}等。區(qū)別于default_if_none,這個(gè)只有在變量為None的時(shí)候才使用默認(rèn)值。first:返回列表中的第一個(gè)值。last:返回列表中的最后一個(gè)值。date:格式化日期和時(shí)間。time:格式化時(shí)間。join:跟python中的join一樣的用法。length:返回字符串或者是數(shù)組的長(zhǎng)度。length_is:字符串或者是數(shù)組的長(zhǎng)度是否是指定的值。lower:把所有字符串都編程小寫。truncatechars:根據(jù)后面給的參數(shù),截?cái)嘧址绻^(guò)了用…表示。{{value|truncatechars:5}},如果value是等于北京歡迎您,那么輸出的結(jié)果是北京...,為啥不是北京歡迎您...呢?因?yàn)槿齻€(gè)點(diǎn)也占了三個(gè)字符,所以北京 + 三個(gè)點(diǎn)的字符長(zhǎng)度就是5。truncatewords:類似truncatechars,不過(guò)不會(huì)切割html標(biāo)簽,{{value|truncatewords:5}},如果value是等于<p>北京歡迎您</p>,那么輸出的結(jié)果是<p>北京...</p>,capfirst:首字母大寫。slice:切割列表。用法跟python中的切片操作是一樣的,區(qū)間是前閉合后開放。striptags:去掉所有的html標(biāo)簽。safe:關(guān)閉變量的自動(dòng)轉(zhuǎn)義,解析html標(biāo)簽。floatformat:浮點(diǎn)數(shù)格式化。更多可以查詢官方文檔:https://yiyibooks.cn/xx/Django_1.11.6/ref/templates/builtins.html英文:https://docs.djangoproject.com/en/1.11/ref/templates/builtins/
過(guò)濾器總結(jié):1、作用:對(duì)變量進(jìn)行過(guò)濾。在真正渲染出來(lái)之前,過(guò)濾器會(huì)根據(jù)功能處理好變量,然后得出結(jié)果后再替換掉原來(lái)的變量展示出來(lái)。2、語(yǔ)法:{{greeting|lower}},變量和過(guò)濾器中間使用管道符號(hào)”|”進(jìn)行使用。3、可以通過(guò)管道符號(hào)進(jìn)行鏈?zhǔn)秸{(diào)用,比如實(shí)現(xiàn)一個(gè)功能,先把所有字符變成小寫,把第一個(gè)字符轉(zhuǎn)換成大寫,如{{message|lower|capfirst}}
4、過(guò)濾器可以使用參數(shù),在過(guò)濾器名稱后面使用冒號(hào)”:”再加上參數(shù),比如要把一個(gè)字符串中所有的空格去掉,則可以使用cut過(guò)濾器,代碼如下{{message|cut:" "}},冒號(hào)和參數(shù)之間不能有任何空格,一定要緊挨著。
雖然DTL給我們內(nèi)置了許多好用的過(guò)濾器,但是有些時(shí)候還是不能滿足我們的需求,因此Django給我們提供了一個(gè)接口,可以讓我們自定義過(guò)濾器,實(shí)現(xiàn)自己的需求。
步驟:
1、首先在某個(gè)應(yīng)用app中,創(chuàng)建一個(gè)python包,叫做templatetags,注意名字不能改動(dòng),不然找不到。
2、在這個(gè)templatetags包下創(chuàng)建一個(gè)python文件用來(lái)存儲(chǔ)過(guò)濾器。
3、在新建的python文件中,定義過(guò)濾器(也就是函數(shù)),這個(gè)函數(shù)的第一個(gè)參數(shù)永遠(yuǎn)是被過(guò)濾器的那個(gè)值,并且如果在使用過(guò)濾器的時(shí)候傳遞參數(shù),那么還可以定義另外一個(gè)參數(shù),但是過(guò)濾器最多只能有2個(gè)參數(shù)。
4、自定義完過(guò)濾器,要使用django.template.Library.filter進(jìn)行注冊(cè)。
5、過(guò)濾器所在app所在app需要在settings.py中進(jìn)行注冊(cè)。
6、在模板中使用load標(biāo)簽加載過(guò)濾器所在的python包。
7、在模板中使用自定義的過(guò)濾器。
我們來(lái)實(shí)現(xiàn)一個(gè)朋友圈,或者文章發(fā)表時(shí)間顯示的過(guò)濾器:
# 自定義過(guò)濾器 date_filter.py
from django import template
from datetime import datetime, timedelta
# 將注冊(cè)類實(shí)例化為register對(duì)象
# register = template.Library() 創(chuàng)建一個(gè)全局register變量,它是用來(lái)注冊(cè)你自定義標(biāo)簽和過(guò)濾器的,只有向系統(tǒng)注冊(cè)過(guò)的tags,系統(tǒng)才認(rèn)得你。
# register 不能做任何修改,一旦修改,該包就無(wú)法引用
register = template.Library()
# 使用裝飾器注冊(cè)自定義過(guò)濾器
@register.filter
def date_filter(value):
# 判斷一下,是不是時(shí)間日期類型
if not isinstance(value, datetime):
return value
now = datetime.now() + timedelta(hours=8) # 將UTC時(shí)間轉(zhuǎn)為本地時(shí)間
# total_seconds()是獲取兩個(gè)時(shí)間之間的總差
timestamp = (now - value).total_seconds()
if timestamp < 60:
return '剛剛'
elif 60 <= timestamp < 60*60: # sec >= 60 and sec < 60*60
mint = int(timestamp / 60)
return '{}分鐘前'.format(mint)
elif 60*60 <= timestamp < 60*60*24:
hour = int(timestamp / 60 / 60)
return '{}小時(shí)前'.format(hour)
elif 60*60*24 <= timestamp < 60*60*24*2:
return '昨天'
else:
return timestamp.strftime('%Y-%m-%d %H:%M:%S')
# 視圖views.py
from django.shortcuts import render, HttpResponse
from datetime import datetime
def index(request):
context = {
'public_time': datetime(year=2020, month=3, day=23, hour=23, minute=0, second=0)
}
print(context)
return render(request, 'index.html', context=context)
# 模板文件
{% load date_filter %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章首頁(yè)</title>
</head>
<body>
{{ public_time|date_filter }}
</body>
</html>
有時(shí)候一些代碼是在許多模板中都用到的,如果每次都重復(fù)的去拷貝代碼那肯定不符合項(xiàng)目的規(guī)范,一般我們可以把這些重復(fù)性的代碼抽取出來(lái),就類似于Python的函數(shù)一樣,以后想要使用這些代碼的時(shí)候,就通過(guò)include包含進(jìn)來(lái),這個(gè)標(biāo)簽就是include。比如大多數(shù)網(wǎng)頁(yè)都有頂部,中間,底部,可能進(jìn)入任何頁(yè)面頂部和底部都是一樣的,只有中間部分內(nèi)容不同,這個(gè)時(shí)候頂部和底部,我們就可以通過(guò)include包含起來(lái)。
# 頂部,header.html
<header>
<ul>
<li>首頁(yè)</li>
<li>Python教程</li>
<li>Python Web開發(fā)</li>
<li>Python爬蟲</li>
</ul>
<p>用戶名:{{ username }}</p> <!-- 用于測(cè)試是否傳參 -->
</header>
# 底部,footer.html
<footer>
<div>?2019<a href="/">全民python</a> |
<a href="http://beian.miit.gov.cn" rel="nofollow">粵ICP備18143893號(hào)</a> |
</div>
</footer>
# 首頁(yè),index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文章首頁(yè)</title>
<style>
div{
margin: 10px;
}
</style>
</head>
<body>
{% include 'header.html' with username='全民python' %} <!-- 引入模板,并用with傳遞參數(shù) -->
<div>
這是中間內(nèi)容
</div>
{% include 'footer.html' %}
</body>
</html>
視圖:
def index(request):
return render(request, 'index.html')
我們使用include標(biāo)簽時(shí),如果引入的html代碼中需要傳入變量,則需要在所引入的view視圖中獲取變量,如果引入的html代碼中的變量是一個(gè)公共變量,則需要重復(fù)獲取N次,使用自定義包含標(biāo)簽inclusion_tag,可以在定義文件中獲取一次即可。
templates/article/my_menu.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定義簡(jiǎn)單標(biāo)簽</title>
</head>
<body>
<h1>-----------菜單欄--------------</h1>
<br/>
{% load menu_tags %} <!-- 導(dǎo)入自定義的標(biāo)簽文件 -->
{% my_menu as menus %} <!-- 注意:這里需要使用as將標(biāo)簽文件返回的值賦給一個(gè)變量,然后再引用!!!-->
<ul>
{% for menu in menus %} {# 企圖使用 {% for menu in my_menu %} 是沒(méi)效果的 #}
<li>{{menu}}</li>
{% endfor %}
</ul>
</body>
</html>
article/templatetags/menu_tags.py
from django import template
from article.models import Column
# register的名字是固定的,不可改變
register = template.Library()
@register.simple_tag
def my_menu():
#menu_list = ['首頁(yè)','項(xiàng)目實(shí)戰(zhàn)','每日一練']
menu_list = Column.objects.all()
return menu_list
templates/article/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首頁(yè)</title>
</head>
<body>
{% include 'article/my_menu.html' %}
<h1>首頁(yè)</h1>
</body>
</html>
兩者感覺(jué)效果一樣。
對(duì)于模板的重復(fù)利用,除了使用include標(biāo)簽引入,還有另外種方式來(lái)實(shí)現(xiàn),那就是模板繼承。模板繼承類似于Python中的類,在父類中可以先定義好一些變量和方法,然后在子類中實(shí)現(xiàn),模板繼承也可以在父模板中先定義好一些子模板需要用到的代碼,然后子模板直接繼承就可以了,并且因?yàn)樽幽0蹇隙ㄓ凶约旱牟煌a,因此可以在父模板中定義一個(gè)block接口,然后子模板再去實(shí)現(xiàn)。
我們將上面那個(gè)例子,使用模板繼承改寫下,將固定不變的頂部和底部,放在父模板中,中間變動(dòng)的部分,子模板去實(shí)現(xiàn)。
<!-- 父模板-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}全民python{% endblock %}</title>
<style>
*{
margin: 0;
padding: 0;
}
header,footer{
background: black;
height: 60px;
width: 100%;
color: white;
text-align: center;
line-height: 40px;
}
footer{
height: 90px;
line-height: 90px;
position: absolute;
bottom: 0;
}
ul li{
list-style: none;
float: left;
margin: 20px;
}
#loginbox{
width: 400px;
height: 200px;
border: 1px solid red;
margin: 90px auto;
text-align: center;
}
</style>
</head>
<body>
<header>
<ul>
<li>首頁(yè)</li>
<li>編程語(yǔ)言</li>
<li>項(xiàng)目實(shí)戰(zhàn)</li>
<li>每日一學(xué)</li>
</ul>
</header>
<div class="content">
{% block content %}
這是子模板自己實(shí)現(xiàn)的部分
{% endblock %}
</div>
<footer >
這是footer部分
</footer>
</body>
</html>
<!-- 子模板-->
{% extends 'base.html' %} <!-- extends必須是模板中的第一個(gè)出現(xiàn)的標(biāo)簽 -->
{% block title %}{{ block.super }}-登錄界面{% endblock %} <!-- block.super繼承父類的內(nèi)容,否則會(huì)覆蓋父類內(nèi)容 -->
{% block content %}
<div id="loginbox">
<br/>
<h2>登錄界面</h2>
<br/>
<form>
帳 號(hào):<input type="text">
<br/>
<br/>
密 碼:<input type="password">
<br/>
<br/>
<input type="submit" value="登錄">
<input type="button" value="注冊(cè)">
</form>
</div>
{% endblock %}
視圖函數(shù):
def index(request):
return render(request, 'index.html')
效果展示:
總結(jié):
模板引入與自定義inclusion_tag的區(qū)別:模板導(dǎo)入的頁(yè)面內(nèi)容是靜態(tài)的、不變的,而通過(guò)自定義inclusion_tag導(dǎo)入的頁(yè)面文件可以是動(dòng)態(tài)的,可動(dòng)性自己掌控。
在一個(gè)網(wǎng)頁(yè)中,不僅僅只有一個(gè)html,還需要css樣式,js腳本以及一些圖片,因此在DTL中加載靜態(tài)文件是一個(gè)必須要解決的問(wèn)題,在DTL中,使用static標(biāo)簽來(lái)加載靜態(tài)文件,要使用static標(biāo)簽,首先需要{% load static %}。
1、首先,settings.py文件中,確保django.contrib.staticfiles已經(jīng)添加到INSTALLED_APPS中。
INSTALLED_APPS = [
...
'django.contrib.staticfiles',
...
]
2、設(shè)置DEBUG調(diào)試模式。
# 在settings.py中,默認(rèn)值是DEBUG = True
DEBUG = True
當(dāng)我們?cè)陂_發(fā)django應(yīng)用時(shí)如果設(shè)置了 DEBUG = True,那么django便會(huì)自動(dòng)幫我們對(duì)靜態(tài)文件進(jìn)行路由;但是當(dāng)我們?cè)O(shè)置DEBUG = False后,靜態(tài)文件找不到了,「img、css、js」都提示404,無(wú)法準(zhǔn)確的訪問(wèn) static 靜態(tài)文件。
3、在開發(fā)模式下(Debug=True)時(shí),訪問(wèn)靜態(tài)文件有下面兩種情況:1)在已注冊(cè)的應(yīng)用app下創(chuàng)建一個(gè)靜態(tài)文件夾名稱static,然后再再這個(gè)static目錄下創(chuàng)建于app同名的 文件夾用于存放靜態(tài)文件(避免不同應(yīng)用app,靜態(tài)文件名字一樣,造成沖突)。
Django將通過(guò) django.contrib.staticfiles在每個(gè)app的static文件夾中為我們自動(dòng)查找這些靜態(tài)文件。
2)如果有些靜態(tài)文件和應(yīng)用沒(méi)什么太大的關(guān)系,我們可以在項(xiàng)目根目錄下再創(chuàng)建static目錄,這個(gè)時(shí)候我們還需要在settings.py文件配置添加STATICFILES_DIRS,以后DTL就會(huì)在這個(gè)列表的路徑中查找靜態(tài)文件。
# django2.x方式
# STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] # 這里指靜態(tài)文件保存哪個(gè)目錄下,這個(gè)“static”指目錄名
# django3.x方式
# 一般用來(lái)設(shè)置通用的靜態(tài)資源,對(duì)應(yīng)的目錄不放在APP下,而是放在Project下,例如:
STATICFILES_DIRS = [BASE_DIR / 'static']
然后再像上面一樣加載靜態(tài)文件使用,一般直接在根項(xiàng)目下新建static,然后在下面新建不同以應(yīng)用名稱命名的目錄。
STATICFILES_DIRS告訴django,首先到STATICFILES_DIRS里面尋找靜態(tài)文件,其次再到各個(gè)app的static文件夾里面找(注意,django查找靜態(tài)文件是惰性查找,查找到第一個(gè),就停止查找了)。
4、只有在生產(chǎn)模式(Debug=False)時(shí),STATIC_ROOT設(shè)置才生效。
STATIC_ROOT 是在部署靜態(tài)文件時(shí)(pyhton manage.py collectstatic)所有的靜態(tài)文靜聚合的目錄,STATIC_ROOT要寫成絕對(duì)地址,如下例子STATIC_ROOT設(shè)為根目錄下的static_new文件,而STATICFILES_DIRS使用根目錄下的static目錄。
#STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] # 這里指靜態(tài)文件保存哪個(gè)路徑,這個(gè)“static”指目錄名
STATICFILES_DIRS = [BASE_DIR / 'static']
if not DEBUG:
#STATIC_ROOT = os.path.join(BASE_DIR, "static_new") #使用 collectstatic后收集的靜態(tài)文件的存放絕對(duì)路徑
STATIC_ROOT = [BASE_DIR / 'static_new']
當(dāng)部署項(xiàng)目時(shí),在終端輸入:
# django會(huì)把所有的static文件都復(fù)制到STATIC_ROOT文件夾下
python manage.py collectstatic
然后可以看到根目錄下有創(chuàng)建了一個(gè)static_new文件夾,里面有STATICFILES_DIRS里設(shè)置的文件夾里的靜態(tài)文件,同時(shí)也有各app下static文件夾里的靜態(tài)文件。
在部署模式(Debug=False)時(shí),Django會(huì)從STATIC_ROOT設(shè)置的文件夾讀取靜態(tài)文件,而不再?gòu)?span style="color: #1E6BB8; --tt-darkmode-color: #1E6BB8;">STATICFILES_DIRS設(shè)置的文件夾或app下的static文件夾。所以在(Debug=False)時(shí)需要先python manage.py collectstatic同步一下靜態(tài)文件。
說(shuō)明:真實(shí)上生產(chǎn),靜態(tài)文件可能就交給nginx了,配置好即可,就不會(huì)再?gòu)膁jango里面讀取靜態(tài)文件了。具體后續(xù)我們會(huì)細(xì)說(shuō)
5、上面我們已經(jīng)配置好靜態(tài)文件了,那怎么樣訪問(wèn)呢?
如果在瀏覽器是訪問(wèn),不可能輸入你的靜態(tài)文件的本地絕對(duì)地址吧,比如我的一種圖片的本地地址為 /root/src/project/QmpythonBlog/static/image/article/article_cover.png,那我們不可能
在瀏覽器上直接輸入:http://127.0.0.1:5044/root/src/project/QmpythonBlog/static/image/article/article_cover.png 這樣子,瀏覽器會(huì)報(bào)錯(cuò), 沒(méi)有該頁(yè)面,那么django是如何讓瀏覽器也可以訪問(wèn)服務(wù)器上的靜態(tài)文件呢?
上面已經(jīng)說(shuō)了,直接訪問(wèn)服務(wù)器本地的地址是不行的,那就需要一個(gè)映射。
django利用STATIC_URL來(lái)讓瀏覽器可以直接訪問(wèn)靜態(tài)文件。
settings.py文件中確保配置了STATIC_URL,STATIC_URL用于引用STATICFILES_DIRS或STATIC_ROOT所指向的靜態(tài)文件。
當(dāng)我們創(chuàng)建Django項(xiàng)目的時(shí)候,在setting.py中默認(rèn)就已經(jīng)設(shè)置了。
# 靜態(tài)文件存儲(chǔ),一般是我們的JS、css、系統(tǒng)的圖片文件等。
# 這個(gè)“static”指訪問(wèn)靜態(tài)文件,引入時(shí)用的別名
# 通過(guò)http://127.0.0.1/static/***就可以訪問(wèn)相關(guān)的靜態(tài)文件了。
STATIC_URL = '/static/'
例如:在應(yīng)用user下新建static目錄,再在static下新建user目錄:
視圖:
# user應(yīng)用下的視圖views.py
def index(request):
return render(request, 'user/index.html')
模板文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用戶登錄首頁(yè)</title>
</head>
<body>
<p>用戶界面</p>
<img src="/static/user/img/logo.png"> <!-- 如果配置文件是 STATIC_URL = '/abc/',則這里url:/abc/user/img/logo.png-->
</body>
</html>
但是上面要寫絕對(duì)路徑不方便,那我們可以通過(guò)static標(biāo)簽來(lái)加載靜態(tài)文件:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用戶登錄首頁(yè)</title>
</head>
<body>
<p>用戶界面</p>
{# <img src="/static/user/img/logo.png">#}
<img src="{% static '/user/img/logo.png' %}"> <!-- 通過(guò)static標(biāo)簽,django會(huì)去static目錄下尋找 -->
</body>
</html>
{% static %}這個(gè)模板變量使用的就是STATIC_URL的值,例如默認(rèn)的STATIC_URL值為/static/,那么上面模板變量引用后的值便為/static/開頭的地址。
那么STATIC_URL又是怎么能正確的找到靜態(tài)文件地址的呢。
1)在開發(fā)模式下(Debug=True)時(shí),使用的是STATICFILES_DIRS和app下的static中的靜態(tài)文件,Django有默認(rèn)的對(duì)STATIC_URL路由能自動(dòng)找到放在里面的靜態(tài)文件。
2)在部署模式(Debug=False)時(shí),使用的是STATIC_ROOT中的靜態(tài)文件,此時(shí)則沒(méi)有了默認(rèn)的對(duì)STATIC_URL的路由,需要自己在project的urls.py中寫一個(gè),將STATIC_URL開頭的請(qǐng)求轉(zhuǎn)發(fā)到STATIC_ROOT中進(jìn)行查找。
from django.views.static import serve
from django.conf.urls.static import static
urlpatterns = [
re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}, name='static'),
]
在部署生產(chǎn)環(huán)境時(shí)(Debug=False),通常使用另外一種方法就是使用Nginx來(lái)實(shí)現(xiàn)。
如果你在項(xiàng)目中用到了static這個(gè)模板標(biāo)簽,那一定要將nginx(或其他)服務(wù)器的/static配置到與STATIC_ROOT一致!(如果執(zhí)行了收集的話)
可以參考django部署配置:Centos7中使用Nginx+uWSGI+Django部署Web項(xiàng)目
此時(shí)我們?cè)趹?yīng)用中加載靜態(tài)文件的話就只需要這么來(lái)寫了,需要將STATIC_URL設(shè)為/static/,以img加載為例:
<img src="/static/images/good.png" alt="My image"/>
額外知識(shí)點(diǎn):
1、如果不想在模板文件中每次都通過(guò){% load static %}加載靜態(tài)文件。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
#'DIRS': [os.path.join(BASE_DIR, 'templates')] # django2寫法
'DIRS': [BASE_DIR / 'templates'], # django3寫法
,
'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',
],
'builtins': ['django.templatetags.static'] # 添加這一行,就會(huì)將static當(dāng)作內(nèi)置的標(biāo)簽,無(wú)需再手動(dòng)load
},
},
]
備注:一般個(gè)人傾向于,直接將static文件放到項(xiàng)目根目錄下,下面再根據(jù)app名字進(jìn)行分門別類。
#Django#
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。