Django
Django模板簡介
Django使用的是MVT模型,而模板就是MVT中的T: templates 模版,也是Django區別與常規的MVC模型不同的地方。模板是前後端交互的一個橋樑,Django就是依靠模板,將參數傳入前端頁面。下列我將介紹,模板一般放置於那些位置,如何讓.html和.py能相互交互。
模板路徑
模板所在路徑,並非絕對的規定,只是一種大部人人都遵守的守則。一般模板都會放在名爲templates
的文件中,而這個文件,可以放在兩個位置,分別是項目根目錄中,或者放在APP的根目錄中(一般項目根目錄中的頁面優先級會大於APP中的)
(如果使用pycharm創建項目,根目錄中的templates會被自動創建,且os.path.join(BASE_DIR, 'templates')
也會自行添加,但APP中的templates文件需要手動註冊,在列表INSTALLED_APPS
添加APP名即可,不過前提是要確定'APP_DIRS': True,
(True爲默認選項)
導入HTML
在Python中導入HTML也有兩種方式。下述信息不在包含如何路由等信息,如何路由可以在Django路由機制詳解中查看
- 方式一: 使用
from django.template.loader import render_to_string
from django.template.loader import render_to_string
from django.http import HttpResponse
# 在此處創建視圖。
def home(request):
html = render_to_string('home.html')
return HttpResponse(html)
這種方式使用較少,暫不詳細講解了,在絕大部分情況下我們都會使用下列方式導入HTML
2. 方式二: 使用from django.shortcuts import render
from django.shortcuts import render
# 在此處創建視圖。
def home(request):
return render(request, 'home.html')
相比於第一種方式,我們發現第二種方式會讓代碼顯得更加簡潔,可讀性更好。
模板變量
變量我們可以簡單看成傳值,使用模板進行傳值操作,在Python中,需要使用到context屬性,這是render與render_to_string都具有的屬性。在HTML中加入{{字典鍵}}
模板變量Python部分: 使用context=字典
(context必須傳入字典dict的數據類型)的方式,如果你需要傳入列表或元組或者其他類型的值,將其當成字典鍵傳入。此處字典的鍵,甚至可以穿入一個類
from django.shortcuts import render
class MyName:
def __init__(self, name):
self.name = name
def get_name(self):
""""""
return '我是類方法'
xunmi = MyName('我是類屬性')
data = {
'暱稱': '尋覓',
'字典': {
'鍵1': 1,
'鍵2': 2,
},
'列表': [
'one',
2,
'三'
],
'類': xunmi
}
# 在此處創建視圖。
def home(request):
# html = render_to_string('home.html')
return render(request, 'home.html', context=data)
模板變量HTML部分: 接收使用{{字典鍵}}
的方式,如下(如果你是一個前端,你可以將需要後端接收變量的地方使用{{}}事先預留,然後想後端詢問字典鍵填入即可事先傳值)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>家</title>
</head>
<body>
<h1>這是{{暱稱}}首頁</h1>
<h1>{{字典.keys}}{{字典.values}}可以使用keys和values等屬性</h1>
<h1>這是列表:{{列表}}</h1>
<h1>這是列表第一個元素:{{列表.0}}</h1>
<h1>這是列表第二個元素:{{列表.1}}</h1>
<h1>這是列表第三個元素:{{列表.2}}</h1>
<h1>這是一個類屬性:{{類.name}}</h1>
<h1>這是一個類方法:{{類.get_name}}</h1>
</body>
</html>
模塊語言(DTL)
在寫上述變量時候,我們如果需要取鍵爲列表或其他可迭代對象時會比較麻煩,如果能使用for語句那將會如虎添翼,
添加代碼模板
代碼模板純屬是方便之後使用,Django的模板在pycharm專業版中自行攜帶,如果你是用的並非是專業版,可以在設置中執行添加
我們可以添加幾個常用的模板,比如說if和for
# if模板
{% if $VARIABLE$ %}
$END$
{% endif %}
# for模板
{% for $VAR$ in $COLLECTION$ %}
$END$
{% endfor %}
添加好模板後,我們如果需要使用只要輸入相應的縮寫,然後在按下tab鍵即會自動補齊
for與if標籤
for與if和Python使用方式大相徑庭,只是需要在使用時,用{% 標籤 %}進行包裹即可,使用比較簡單,這裏就合併一起進行講解。HTML和Python中唯一不同的是,在結束時候我們需要使用一個結束標籤來標記,其中if的結束標籤爲{% endif %}
而for的結束標籤爲{% endfor %}
在for循環中,模板語言提供了一些變量可供使用
變量 | 作用 |
---|---|
forloop.counter |
當前循環的次數(從1開始計算) |
forloop.counter0 |
當前循環的下標(從0開始計算) |
forloop.revcounter |
剩餘循環的次數(從總循環次數開始計算) |
forloop.revcounter0 |
剩餘循環的次數(從總循環次數-1開始計算) |
forloop.first |
是否爲第一次循環 |
forloop.last |
是否爲最後一次循環 |
forloop.parentloop |
輸出父循環的信息(循環次數,剩餘次數,是否爲第一次、最後一次循環,如果其父循環存在父循環,則會一同輸出) |
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>家</title>
</head>
<body>
{% for foo in 列表 %}
{% for foo1 in 字典 %}
{% for foo2 in 暱稱 %}
<p>{{ forloop.parentloop }}</p>
<p>{{ foo2 }}</p>
{% endfor %}
<p>{{ foo1 }}</p>
{% endfor %}
{% if foo == '2' %}
<h3>我是加粗的二貨</h3>
{% else %}
<p>我是列表:{{ foo }}</p>
{% endif %}
{% endfor %}
</body>
</html>
再次需要注意的是for循環語句中的break與continue無法使用,且沒有whlie循環
url標籤
url標籤將會是我們非常常用的標籤之一,雖然我們可以將標籤寫死在頁面中,但聰明的程序員絕對不會這樣幹,因爲這樣既不利於後期的維護。
這裏url類似與Django路由中的反轉機制,需要我們現在path中寫入name,然後在基於此name進行跳轉,在跳轉同時,我們也可以攜參跳轉,兩種傳參方式下列代碼中也都有演示。
url標籤的Python代碼
(Django項目APP中urls.py文件中代碼):
from django.urls import path
from . import views
app_name = 'front'
urlpatterns = [
path('home/', views.home, name='home'),
path('home/<items>/', views.home, name='home'),
path('index/', views.index, name='index'),
]
Django項目APP中views.py文件中代碼
from django.shortcuts import render
# 在此處創建視圖。
def home(request, items='默認值'):
# html = render_to_string('home.html')
data = {'參數': items}
return render(request, 'home.html', context=data)
def index(request):
uid = request.GET.get('id')
data = {'參數': uid}
return render(request, 'index.html', context=data)
url標籤的HTML中代碼
home.html:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>家</title>
</head>
<body>
<h1>攜帶了"{{ 參數 }}"歸來了</h1>
<a href="{% url 'front:index' %}">立刻馬上來點我</a>
<br>
<a href="{% url 'front:index' %}?id=1">我是問號傳參</a>
</body>
</html>
index.html:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>我就是一測試頁面</title>
</head>
<body>
<p>我真的就是一個測試頁面,不要過來啊</p>
{% if 參數 %}
<h1>你居然將參數"{{ 參數 }}"插入了我</h1>
{% endif %}
<a href="{% url 'front:home' '位置參數' %}">快按我跳回去(位置參數)</a>
<br>
<a href="{% url 'front:home' items='關鍵字參數' %}">快按我跳回去(關鍵字參數)</a>
</body>
</html>
模板過濾器
過濾器的作用是在HTML頁面中將傳入的參數進行過濾,留下有用的,這個操作雖然可以在Python代碼中完成,但有時候我們會發現在HTML中放入過濾器效果會更好,下面我會簡單介紹一些常用的過濾器。如果你想了解全部的過濾器可以自行去from django.template import defaultfilters
此庫中查看過濾器源碼。
add(拼接)
此拼接主要用於字符串(str)與整數(int),浮點型(float)會自動被轉換成整數(int),小數部分自動省去。
將傳入的值拼接到原值上,這個過濾器比較智能,如果兩個變量都爲數字,他會將其相加,無論是否爲整數類型(如果是字符串類型的數字會將其自動轉換爲整數)如果是字符串則會進行字符的拼接。這裏需要注意的是Python是強類型語言,所以不同字符類型的內容無法相加,比如說2+'億'
爲空值,須寫成'2'+'億'
!
<!--列表.1: '2' ; 字典.鍵2: 2-->
<p>我是add過濾器:'{{ 列表.1 | add:字典.鍵2 }}'或'{{ '3' | add:'億' }}'</p>
join(拼接)
將可迭代對象進行拼接(比如說列表,元組,字符串),用法類似與Python中的join
<!--列表=['one', '2', '三']-->
<p>我是join過濾器:{{ 列表 | join:' - ' }}</p>
silce(切片)
和Python中列表等可迭代對象切片方式類似,格式都爲(起始角標:結束角標:步長),比如下列切片就將其中的兩個逗號成功切分出來了
<!--'切片使用': '你好,我是,尋覓'-->
<p>我是slice過濾器:{{ 切片使用 | slice:'2:6:3' }}</p>
cut(移除)
移除值中所以指定的字符串,類似於Python中的reqlace(args,’’)
<!--暱稱: '尋覓'-->
<p>我是cut過濾器:'{{ 暱稱 | cut:'尋' }}'</p>
date(時間)
將一個日期按指定格式格式化成字符串
<!--'時間': datetime.now(), datetime.now()是Python內置庫中的方法,會讀取當前時間,格式樣式(/ :)可以自定義-->
<p>我是date過濾器:原始樣式-'{{ 時間 }}'過濾器轉換後-'{{ 時間 | date:'Y/m/d G:i:s' }}'</p>
常用格式字符 | 描述 | 示例 |
---|---|---|
Y | 四位數字年份 | 2020 |
m | 兩位數字月份 | 01-12 |
n | 一位或兩位數字月份 | 1-12 |
d | 兩位數字天數 | 01-31 |
j | 一位或兩位數字天數 | 1-31 |
g | 帶前綴0的十二小時制小時 | 01-12 |
h | 不帶前綴0的十二小時制小時 | 1-12 |
G | 帶前綴0的二十四小時制小時 | 00-23 |
H | 不帶前綴0的二十四小時制小時 | 0-23 |
i | 帶前綴0的分鐘 | 00-59 |
s | 帶前綴0的秒鐘 | 00-59 |
default(默認值)
當值被評估爲Falst時會使用default所提供的默認值([],{},None,Falst等都會被評估爲Falst)
<!--空值=None-->
<p>我是default過濾器:{{ 空值 | default:'此處無值' }}</p>
flast(返回首元素)與last(返回尾元素)
返回可迭代對象的第一個元素|最後一個元素
<!--列表=['one', '2', '三']-->
<p>我是first與last過濾器:列表第一個元素-{{ 列表 | first }} | 列表最後一個元素-{{ 列表 | last }}</p>
floatformat(移除)
使用四捨五入的方式格式化一個浮點類型(默認保留一位小數,如果小數部分全爲0則只保留整數,可在其後指定保留位數)
<p>我是floatformat過濾器:{{ 123.456 | floatformat }}或{{ 123.456 | floatformat:2 }}</p>
length(獲取長度)
獲取長度(可以看成可迭代對象的迭代次數),和Python中的len()
作用相同
<!--暱稱='尋覓', 列表=['one', '2', '三']-->
<p>我是length過濾器:暱稱-{{ 暱稱 | length }} | 列表-{{ 列表 | length }}</p>
lower(強制轉換爲小心)與upper(強制轉換爲大寫)
移除值中所以指定的字符串,類似於Python中的reqlace(args,’’)
<!--'大小寫轉換': "AaBb"-->
<p>我是lower與upper過濾器:強制小寫-{{ 大小寫轉換 | lower }} | 強制大寫-{{ 大小寫轉換 | upper }}</p>
safe(標記安全字符串)
Django會默認將傳入的值進行轉義,以防SQL注入攻擊,如果傳入字符被標記爲安全字符串,則Django不會再將其轉義。
<!--'js腳本': '<script>alert(document.domain)</script>'-->
<p>我是safe過濾器:此腳本是{{ js腳本 }},在刷新網頁時會執行{{ js腳本 | safe }}</p>
striptags(刪除標籤)
刪除值中所帶的HTML標籤
<!--'js腳本': '<script>alert(document.domain)</script>'-->
<p>我是striptags過濾器:{{ js腳本 | striptags }}</p>
truncatechars(指定長度)
指定長度後只能顯示指定長度的內容,如果超過指定內容,則會已省略號代替(比如說指定字符長度爲5,如果傳入的字符串大於等於5,那麼第5個字將會被省略號代替,之後的字符將會被省略)
<!--'js腳本': '<script>alert(document.domain)</script>'-->
<p>我是truncatechars過濾器:{{ js腳本 | truncatechars:10 }}</p>
模板優化
引入模板
一個站點中的有頁眉和頁尾,爲了避免重複性導入,我們可以寫一個模板,在其他頁面引入即可,這樣可以避免重複寫大量代碼的同時,也增強了代碼的可維護性,如果進行繼承,這裏就需要用到{% include 'HTML頁面' %}
將父類引入即可。
如果需要傳參,可以通過with進行{% include 'HTML頁面' with 參數名='參數' %}
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>我就是一測試頁面</title>
</head>
<body>
{% include 'header.html' %}
<p>我真的就是一個測試頁面,不要過來啊</p>
{% include 'footer.html' with 暱稱='xunmi' %}
<!--footer.html中的代碼爲: <p>我是尾部footer.html文件{{ 暱稱 }}</p>-->
</body>
</html>
繼承模板
繼承和上述的引用想要達成的目的大致相同,都是爲了減少重複代碼而出現的,不過繼承比引入更加適用,模板中的繼承和Python的繼承有異曲同工之處,在父模板中定義一些子模板需要用到的代碼,然後子模板自己繼承即可
父模板使用方式
在HTML中可以預留<% block 填充名 %>
<% endblock %>
標註子模板需要填充的區域。
列如:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>父模板頁面</title>
</head>
<body>
<h1>我是父模板</h1>
{% block 填充一 %}
<p>這裏的代碼默認會被覆蓋</p>
{% endblock %}
<h2>上方是第一個子模板</h2>
{% block 填充二 %}
<p>這裏的代碼默認會被覆蓋</p>
{% endblock %}
<h3>上方是第二個子模板填充區</h3>
</body>
</html>
子模板使用方式
子模板需要先導入父模板,使用{% extends '父模板名.html' %}
即可導入父模板。 導入父模板後,同樣是使用<% block 填充名 %>
<% endblock %>
方法即可在父模板中預留的位置填充內容(填充的內容會覆蓋父模板中block區域中的內容,可以使用{{ block.super }}
方法放在填充區中防止覆蓋),填充名除了可以在block中寫入,在endblock中也可以寫入,此功能在大項目中可以增強代碼的可讀性。
例如:
{% extends 'inherit.html' %}
{% block 填充一 %}
<p>我是在父模板的填充一區</p>
{% endblock %}
{% block 填充二 %}
{{ block.super }}
<p>我是獲得了填充二區內容的區域</p>
{% endblock 填充二 %}
引入靜態文件
一個網站中除了有HTML,一些靜態文件也是不可或缺的,比如css、js、圖片等。這些今天文件我們可以直接寫絕對路徑,或相對路徑來表示,但這種做法並不是明智的,如果一個靜態文件被多次引入,在其後修改時就會造成巨大的工程量,而且可能會出現漏改的情況。
settings.py文件設置
下方設置都在settings.py下完成:
- 確定是否在
INSTALLED_APPS
中安裝了'django.contrib.staticfiles',
(高版本Django默認安裝) - 確保已經指定
STATIC_URL = '/static/'
(static爲文件夾名,可以自定義,此設定在settings.py文件最下方,同樣在高版本Django會默認設置)
確保有上述兩部,我們就可以正常使用靜態文件了,但是在使用過程會較爲麻煩,我們可以在進行接下來幾部操作進行優化: - 當前設置我們只能APP目錄下的
static
文件夾中的內容,但有些靜態文件是公用的,我們可能想把它放入根目錄中,這時候就需要手動寫入(推薦放在settings.py最下方STATIC_URL = '/static/'之後,方便查看)
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
- 默認情況下,我們在HTML中每次使用靜態文件都需要在HTML中寫入
{% load static %}
引入靜態文件夾。比較麻煩,不過可以在設置中的TEMPLATES
裏的OPTIONS
屬性內添加'builtins': ['django.templatetags.static'],
操作完成上述步驟,我們使用{% static '靜態文件名' %}
的方式即可引入文件
引入示例
我現在項目的APP與根目錄中的static文件夾中分別放入了一張圖片與一個css(其中設置背景顏色爲猛男色)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="{% static 'css.css' %}">
</head>
<body>
<img src="{% static '炮姐.png' %}" alt="" width="200">
</body>
</html>