Django基礎之:模版系統


一 引子

什么是模版系統?這里做一個簡單解釋。要想明白什么是模版系統,那么我們得先分清楚靜態頁面和動態頁面。我們之前學過的都是靜態頁面,所謂的靜態頁面就是瀏覽器向后端發送一個請求,后端接收到這個請求,然后返回給瀏覽器一個html頁面,這個過程不涉及從數據庫取出數據渲染到html頁面上,只是單純的返回一個頁面(數據全部在html頁面上)。而動態頁面就是在給瀏覽器返回html頁面之前,需要后端與數據庫之間進行數據交互,然后將數據渲染到html頁面上在返回給瀏覽器。言外之意靜態頁面不涉及數據庫,動態頁面需要涉及從數據庫取出數據。那么模版系統是什么呢?如果你只是單純的寫靜態頁面,也就沒必有必要用模版系統了,只用動態頁面才需要模版系統。

簡單來說,模版系統就是在html頁面想要展示的數據庫或者后端的數據的標簽上做上特殊的占位(類似於格式化輸出),通過render方法將這些占位的標簽里面的數據替換成你想替換的數據,然后再將替換數據之后的html頁面返回給瀏覽器,這個就是模版系統。

模板渲染的官方文檔

關於模板渲染你只需要記兩種特殊符號(語法):

{{  }}和 {% %}

變量相關的用{{}},邏輯相關的用{%%}。

 

二、變量

1. 簡單示例

接下來,我們先搭一個簡單流程:瀏覽器訪問https://127.0.0.1:8000:/index,返回一個index.html頁面,我們在views函數中設置一些變量,然后通過模版系統渲染,最終返回給瀏覽器。

url:

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^index/',views.index),
]

views:

render第三個參數接受一個字典的形式,通過字典的鍵值對index.html頁面進行渲染,這也就是模塊渲染。

def index(request):
    name = '太白金星'
    age = 18
    name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥']
    dic = {'classname': '人工智能', 'since': 2019}

    return render(request, 'index.html',{'name': name, 'age': age,'name_list':name_list, 'dic_class':dic})

html:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>

<ul>
    {#  通過render進行模版語言渲染然后替換成后端的數據,最終發給瀏覽器  #}
    <li>{{ name }}</li>
    <li>{{ age }}</li>
    <li>{{ name_list }}</li>
    <li>{{ dic_class }}</li>
</ul>
</body>
</html>

最終瀏覽器顯示的結果為:

 

這樣,你后端這些變量全部都渲染到前端了。

2. 語法

在Django的模板語言中按此語法使用:{{ 變量名 }}。

  當模版引擎遇到一個變量,它將計算這個變量,然后用結果替換掉它本身。 變量的命名包括任何字母數字以及下划線 ("_")的組合。 變量名稱中不能有空格或標點符號。

  深度查詢據點符(.)在模板語言中有特殊的含義。當模版系統遇到點("."),它將以這樣的順序查詢:

    字典查詢(Dictionary lookup)
    屬性或方法查詢(Attribute or method lookup)
    數字索引查詢(Numeric index lookup)

3. 萬能的點 . 

通過簡單示例我們已經知道模版系統對於變量的渲染是如何做到的,非常簡單,接下來我們研究一些深入的渲染,我們不想將整個列表或者字典渲染到html,而是將列表里面的元素、或者字典的某個值渲染到html頁面中,那怎么做呢?就是通過萬能的點。

views:

def index(request):
  
    name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥']
    dic = {'classname': '人工智能', 'since': 2019}
    lis = [1, ['冠狀病毒', '武漢加油'],3]

    # 使用locals是用於測試,實際生產環境中是不可以的。
    return render(request, 'index.html', locals())

html:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>

<ul>

    {# 萬能的點 #}
    <li>{{ name_list.2 }}</li>
    <li>{{ dic.classname }}</li>
    <li>{{ lis.1.0 }}</li>
</ul>
</body>
</html>

瀏覽器的顯示結果:

剛才我們嘗試的數據類型,那么在一切皆對象的python世界中,我們一個對象是否可以通過模版渲染到html頁面中呢?

views:

def index(request):
  
    class A:

        def __init__(self):
            self.name = 'barry'

        def func(self):
            return '太白教你學python'

    obj = A()
    return render(request, 'index.html', locals())

html:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>

<ul>
   
    {# 萬能的點 #}

    <li>{{ obj.name }}</li>
    <li>{{ obj.func }}</li>
</ul>
</body>
</html>

瀏覽器顯示的結果:

 

如圖所示,對象也是可以通過模版渲染到html頁面中的,但是這里要注意一個點:obj.func是不可以加括號的,所以我們后端設置的函數是不可以有參數的(類中的方法self自動傳遞)。

注意我們直接在js代碼中使用模板語法的時候,模板渲染的時候會有個轉義的動作,將s = ['哈哈','xx']這種數據中的元素的引號變為一個特殊符號:這個我們后面會講到

    <script>
        // 不加safe的話,引號會被轉義。
// var a = {{ s }} // var a = [&#39;哈哈&#39;, &#39;xx&#39;]; // console.log(a[0]) // 加上safe就正常了 var a = {{ s|safe }}; console.log(a[0])
// 還要注意,當我們模板渲染的時候,后端返回的數據是字符串的話,我們需要將{{ s }}外面加上引號
比如s = '哈哈'
js中的寫法
var a = '{{ s }}' </script>

三、過濾器

1.什么是過濾器

有的時候我們通過render渲染到html的數據並不是我們最終想要的數據,比如后端向前端傳遞的數據為hello,但是我們html想要顯示為HELLO,當然這個在后端是可以提前處理的,但是諸如此類的需求我們可以通過過濾器解決,過濾器給我們提過了很多便捷的方法,加工你傳遞到html的數據,便於靈活開發。

2.語法

  過濾器的語法: {{ value| filter_name:參數 }}

  使用管道符"|"來應用過濾器。

  例如:{{ name| lower }}會將name變量應用lower過濾器之后再顯示它的值。lower在這里的作用是將文本全都變成小寫。

  注意事項:

  1. 過濾器支持“鏈式”操作。即一個過濾器的輸出作為另一個過濾器的輸入。
  2. 過濾器可以接受參數,例如:{{ sss|truncatewords:30 }},這將顯示sss的前30個詞。
  3. 過濾器參數包含空格的話,必須用引號包裹起來。比如使用逗號和空格去連接一個列表中的元素,如:{{ list|join:', ' }}
  4. '|'左右沒有空格!沒有空格!沒有空格!

  Django的模板語言中提供了大約六十個內置過濾器。

3.常用過濾器

default: 如果一個變量是false或者為空,使用給定的默認值。 否則,使用變量的值。

views:
a = ''  # 沒有變量a或者變量a為空

html: # 顯示啥也沒有 {{ value|default:"啥也沒有"}}

length:返回值的長度,作用於字符串和列表。

views:
name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥']
s = '太白金星講師'
html:
{{ name_list|length }}  # 顯示為5
{{s|length }}  # 顯示為6

filesizeformat: 將值格式化為一個 “人類可讀的” 文件尺寸 (例如 '13 KB''4.1 MB''102 bytes', 等等)。

views:
value = 1048576

html:
{{ value|filesizeformat }}  # 顯示為1.0MB

slice:切片,支持pyhton中可以用切片的所有數據類型

views:
name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥']
s = '太白金星講師'

html:
{{ name_list|slice:'1:3' }}  # ['天琪', '傻強']
{{ s|slice:'1::2' }}  # '白星師'

date:時間格式化

views:
time = datetime.datetime.now()

html:
{{ t|date:"Y-m-d H:i:s" }}  # 2020-02-11 07:31:29

 關於時間日期的可用的參數(除了Y,m,d等等)還有很多,有興趣的可以去查查看看。

truncatechars:如果字符串字符多於指定的字符數量,那么會被截斷。截斷的字符串將以可翻譯的省略號序列(“...”)結尾。

參數:截斷的字符數

views:
describe = '1999年3月,馬雲正式辭去公職,后來被稱為18羅漢的馬雲團隊回到杭州,湊夠50萬元人民幣'

html:
{{ describe|truncatechars:9 }}  # 1999年3...

# 截斷9個字符,三個點也算三個字符

這個我們在瀏覽網頁時經常見到,描述的內容很多,只能用...顯示,比如:

 

 

 

truncatewords:在一定數量的字后截斷字符串,是截多少個單詞。

views:
words = 'i love you my country china'

html:
{{ words|truncatewords:3 }}  # i love you...

cut: 移除value中所有的與給出的變量相同的字符串

views:
words = 'i love you my country china' html: {{ words|cut:3 }} # iloveyou

join: 設定連接符將可迭代對象的元素連接在一起與字符串的join方法相同。

views:
name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥']
dic = {'name':'太白','age': 18}
tu = (1, 2, 3)

html:
<li>{{ name_list|join:'_'}}</li>
<li>{{ dic|join:','}}</li>
<li>{{ tu|join:'+'}}</li>
'''
王闊_天琪_傻強_志晨_健身哥
name,age
1+2+3
'''

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攻擊的根本途徑,例如博客園:

      

timesince(了解)

將日期格式設為自該日期起的時間(例如,“4天,6小時”)。

采用一個可選參數,它是一個包含用作比較點的日期的變量(不帶參數,比較點為現在)。 例如,如果since_12是表示2012年6月28日日期實例,並且comment_date是2018年3月1日日期實例:

views:
  year_12 = datetime.datetime.now().replace(year=2012, month=6, day=28)
  year_18 = datetime.datetime.now().replace(year=2018, month=3, day=1)

html:
 <li>{{ year_12|timesince:year_18}}</li>

'''
5 years, 8 months
'''

如果year_18不寫,默認就是距現在的時間段。

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 }}

  這里簡單介紹一些常用的模板的過濾器,更多詳見

更多內置過濾器(此鏈接頁面最下面的內置過濾器):https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#filters 

四、標簽Tags

現在我們已經可以從后端通過模版系統替換掉前端的數據了,但是如果只是替換掉數據,確實不夠靈活,比如,我們要想在前端頁面展示可迭代對象name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥']每個元素,你如和展示呢?

# 前端頁面
<ul>
    <li>{{ name_list.0 }}</li>
    <li>{{ name_list.1 }}</li>
    <li>{{ name_list.2 }}</li>
    <li>{{ name_list.3 }}</li>
</ul>

這樣寫明顯很low,我們要是可以用上for循環就好了。Django這么強大的框架,不可能想不到這點的,這里模版系統給我們提供了另一種標簽,就是可以在html頁面中進行for循環以及if判斷等等。

標簽看起來像是這樣的: {% tag %}。標簽比變量更加復雜:一些在輸出中創建文本,一些通過循環或邏輯來控制流程,一些加載其后的變量將使用到的額外信息到模版中。與python語法不同的是:一些標簽需要開始和結束標簽 (例如{% tag %} ...標簽 內容 ... {% endtag %})。

學習下面幾種標簽之前,我們要重新寫一個url、views以及html,方便分類學習不與上面的變量產生沖突。

urls:
    url(r'^tags/',views.tags),


views:
def tags(request):
    num = 10
value = '' name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥'] dic = {'name': '太白金星', 'age': 18} return render(request, 'tags.html', locals()) 先創建一個簡單的tags.html即可。

for標簽

基本語法:

{% for 變量 in render的可迭代對象 %}
        {{ 變量 }}
{% endfor %}

例如:
{% for foo in name_list %}
        {{ foo }}
{% endfor %}

便捷用法:

遍歷每一個元素:  寫個for,然后 tab鍵自動生成for循環的結構,循環很基礎,就這么簡單的用,沒有什么break之類的,復雜一些的功能,你要通過js。可以利用{% for obj in list reversed %}反向完成循環。

遍歷一個列表

<ul>
    {% for foo in name_list %}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>

反向遍歷一個列表

{% for foo in name_list reversed %}
        <li>{{ foo }}</li>
{% endfor %}

遍歷一個字典:有items、keys、values參數

<ul>

    {% for key,value in dic.items %}
        <li>{{ key }}: {{ value }}</li>
    {% endfor %}

    {% for key in dic.keys %}
        <li>{{ key }}</li>
    {% endfor %}

    {% for value in dic %}
        <li>{{ value }}</li>
    {% endfor %}

</ul>

forloop的使用

模版系統給我們的for標簽還提供了forloop的功能,這個就是獲取循環的次數,有多種用法:

forloop.counter            當前循環的索引值(從1開始),forloop是循環器,通過點來使用功能
forloop.counter0           當前循環的索引值(從0開始)
forloop.revcounter         當前循環的倒序索引值(從1開始)
forloop.revcounter0        當前循環的倒序索引值(從0開始)
forloop.first              當前循環是不是第一次循環(布爾值)
forloop.last               當前循環是不是最后一次循環(布爾值)
forloop.parentloop         本層循環的外層循環的對象,再通過上面的幾個屬性來顯示外層循環的計數等

我們通過name_list示例:

<ul>
    {% for foo in name_list %}

        <li>{{ forloop.counter }} {{ foo }}</li>
    {% endfor %}

    {% for foo in name_list %}
        {{ forloop.counter0 }}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>

 forloop.counter0數字與元素不在一行是因為我html標簽擺放的問題,與方法無關。

{% for foo in name_list %}
    <li>{{ forloop.revcounter }} {{ foo }}</li>
 {% endfor %}

{% for foo in name_list %}
        <li>{{ forloop.first }} {{ foo }}</li>
{% endfor %}

 

forloop.first、forloop.last多用於下面我們講到if判斷條件。 forloop.parentloop我們講到if標簽在演示。

for...empty...組合 

如果遍歷的可迭代對象是空的或者就沒有這個對象,利用這個組合可以提示用戶。

<ul>        
    {% for foo in aaa %}
        <li>{{ foo }}</li>
    {% empty %}
        <li>查詢的內容啥也沒有</li>
    {% endfor %}

    {% for foo in value %}
        <li>{{ foo }}</li>
    {% empty %}
        <li>查詢的內容啥也沒有</li>
     {% endfor %}
</ul>

if標簽

基本語法:

 {% if %}會對一個變量求值,如果它的值是“True”(存在、不為空、且不是boolean類型的false值),對應的內容塊會輸出。

{% if 條件 %}
    結果  <!--不滿足條件,不會生成這個標簽-->
{% elif 條件 %}
    結果
{% else %}  <!--也是在if標簽結構里面的-->
    結果
{% endif %}

elif和else一定要在if endif標簽里面,設置多個elif或者沒有elif、有沒有else都可以。

示例:

{% if dic.age > 18 %}
    <p>可以干點兒該做的事兒了~</p>
{% elif dic.age < 18 %}
    <p>小孩子,懂什么</p>
{% else %}
    <p> 風華正茂的年齡~</p>
{% endif %}

條件也可以與過濾功能配合

% if name_list|length > 4 %}
    <p>列表元素超過4個</p>
{% else %}
    <p>列表元素太少!</p>
{% endif %}

條件也可以加邏輯運算符

if語句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判斷,注意條件兩邊都有空格。

{% if name_list|length > 4 and '王闊' in name_list %}
    <p>條件都滿足</p>
{% endif %}

with

使用一個簡單地名字緩存一個復雜的變量,多用於給一個復雜的變量起別名,當你需要使用一個“昂貴的”方法(比如訪問數據庫)很多次的時候是非常有用的,記住!等號的左右不要加空格!!

{% with total=business.employees.count %}
    {{ total }} <!--只能在with語句體內用-->
{% endwith %}

{% with business.employees.count as total %}
    {{ total }}
{% endwith %}

forloop.first、forloop.last、forloop.parentloop

當時講for循環時有三個方法,我們沒有嘗試,因為需要與if條件配合。

forloop.first

{% for foo in name_list %}
    {% if forloop.first %}
        {{ foo }}
    {% else %}
        <p>只有第一次循環打印</p>
    {% endif %}
{% endfor %}

forloop.parentloop : 一定注意!他是返回本此循環的外層循環對象,這個對象可以調用forloop的各種方法進行獲取相應的數據。

測試此方法,我們要在views函數中加一個數據類型:lis = [['A', 'B'], ['C', 'D'], ['E', 'F']]

{% for i in lis %}
     {% for j in i %}
{#         <p>{{ forloop.parentloop }}</p>#}
        <p>{{ forloop.parentloop.counter }} {{ forloop.counter }} {{ j }}</p>
     {% endfor %}
{% endfor %}

注意事項

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的值。

csrf_token標簽

  這個標簽是不是非常熟悉?之前我們以post方式提交表單的時候,會報錯,還記得我們在settings里面的中間件配置里面把一個csrf的防御機制給注銷了啊,本身不應該注銷的,而是應該學會怎么使用它,並且不讓自己的操作被forbiden,通過這個標簽就能搞定。

接下來我們重寫構建一個流程:

urls、views、html:

urlpatterns = [
    url(r'^login/',views.login),
]


from django.shortcuts import render, HttpResponse, redirect
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        print(request.POST)  # 這里我們就不進行驗證了,只是研究csrf_token
        return HttpResponse('登錄成功!!!')


<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h3>登錄頁面</h3>

<form action="" method="post">
    用戶名:<input type="text" name="username">
    密碼:<input type="text" name="password">
    <button>提交</button>
</form>
</body>
</html>
View Code

注意:post、get請求不一定非要寫在一個視圖函數中,那么我們之前為什么寫在一個視圖函數中,就是因為post、get都是請求相關的消息,所以我們都用一個視圖函數去處理,你也可以寫在兩個視圖函數中,自己可以嘗試一下。

此時我們開啟項目,通過瀏覽器進行訪問,當你點擊提交時,瀏覽器返回這樣一個畫面:

 

這就是csrf安全機制給你做出的響應。我們之前都是找到settings給對應的中間件注釋掉,但是現在我們要研究一下如何遵循這個安全機制,只要我們在你的form表單中加上一個csrf_token標簽就行了。

 

那么這個標簽起到了什么作用呢?他的原理是什么?我們再次刷新這個頁面,看一下源碼:

當我們加上此標簽之后,再次發出get請求,render返回給我們的頁面中多了一個隱藏的input標簽,並且這個標簽里面有個一鍵值對:

鍵:name,值:隨機的一堆密文。 那么這個是干什么用的呢?其實他的流程是這樣的:

第一次發送get請求,在views視圖函數返回給你login.html頁面之前,render會將csrf_token標簽替換成一個隱藏的input標簽,此標簽的鍵值對就是上面那個鍵值對並且Django將這個鍵值對保存在內存;當你再次進行post請求時,他會驗證你的form表單里面的隱藏的input標簽的鍵值對是否與我內存中存儲的鍵值對相同,如果相同,你是合法的提交,允許通過;如果不相同,則直接返回給你forbidden頁面。這就好比說,第一次get請求,他返回你一個蓋戳的文件,當你在進行post請求時他會驗證是不是那個蓋戳的文件。他的目的就是你提交post請求時,必須是從我給你的get請求返回的頁面提交的。為什么這么做呢?是因為有人登錄你的頁面時是可以繞過的get請求返回的頁面直接進行post請求登錄的,比如說爬蟲。直接通過requests.post('/login/')直接請求的,這樣是沒有csrftoken的,這樣就直接拒絕了。

說了這么多,目的就是一個:驗證當你post提交請求時,是不是從我給你(你通過get請求的)頁面上提交的數據。

那么接下來,我們寫一個簡單的爬蟲驗證一下:

import requests

ret = requests.post('http://127.0.0.1:8000/login/',
                    data={'username': 'taibai', 'password': '123'}
                    )

print(ret.content)

如果你保留這這個驗證,則通過爬蟲是登錄不成功的,只能返回你一個forbidden的html頁面。

如果你將settings那個中間件注釋掉,那么就可以成功訪問了:

五、模版繼承

1. 引子

什么是模版繼承?將這兩個字拆開我們都清楚,模版就是django提供的用於html發送瀏覽器之前,需要在某些標簽進行替換的系統,而繼承我們立馬就會想到這是面向對象的三大特性之一。其實模版繼承就是擁有這兩個特性的模版的高級用法。先不着急直接上知識點,我們用一個例子引出模版繼承。

我們經常訪問一些網站,你會發現只要是一個網站的多個html頁面,他們有一部分是一模一樣的,剩下的部分是本頁面獨有的。比如我們經常使用的博客園:

這個是我博客園的首頁:

 

 

然后我們隨機點一篇博客進去:

個人博客園設定了博客園的樣式之后,你所有的博客都是按照這個樣式創建的,在我選擇的主題這里:導航條和側邊欄布局是一樣的。再比如如果是公司內部的xx管理系統,更是如此。我們打開bootstraps網站(起步):

一般管理系統都是這樣的布局,這個就是固定的導航條和側邊欄。無論我點擊側邊欄里的那個按鈕,這兩部分不會更換只會改變中間的內容。

那么接下來我們實現一個這樣的布局。

我們要准備4個html頁面:base.html、menu1.html、menu2.html、menu3.html,這四個頁面的導航條與左側側邊欄一樣,每個頁面對應一個url。並且每個頁面的左側側邊欄菜單一、菜單二、菜單三可以實現跳轉:跳轉到menu1.html、menu2.html、menu3.html三個頁面。而頂端導航條只是樣式即可。接下來借助於Django,我們實現這四個頁面並對應urls可以跑通流程。

urls:

urlpatterns = [
    url(r'^base/', views.base),
    url(r'^menu1/', views.menu1),
    url(r'^menu2/', views.menu2),
    url(r'^menu3/', views.menu3),

]

views:

def base(request):
    return render(request, 'base.html')


def menu1(request):
    return render(request, 'menu1.html')


def menu2(request):
    return render(request, 'menu2.html')


def menu3(request):
    return render(request, 'menu3.html')

html:

base.html:
<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於我們</a>
    <a href="">預約電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">
    首頁
</div>


</body>
</html>


menu1.html:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於我們</a>
    <a href="">預約電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">
    菜單一首頁
</div>


</body>
</html>

menu2.html:
<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於我們</a>
    <a href="">預約電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">
    菜單2首頁
</div>


</body>
</html>

menu3.html

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於我們</a>
    <a href="">預約電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">
    菜單三首頁
</div>


</body>
</html>
base menu1 menu2 menu3

上面我的的需求雖然完成了但是有沒有什么沒問題?你會發現html重復代碼太多了,如果領導不瞎,最晚后天你就可以領盒飯了。

2. 母版繼承示例

所以針對與我上面的需求,很顯然你現在所擁有的知識點已經解決不了了。那么接下來就是本節的重點:模版繼承。根據這個知識點名字的特點,我們應該想到,我們可不可以建立一個父類,然后讓所有的子孫類都繼承我的父類,這樣我就可以節省很多代碼了,讓我的代碼非常的清新、簡單。這里的父類就不叫父類了他有一個專有名詞:母版。

接下來我們先創建一個母版。(最好不要將base頁面直接作為母版,母版就是只設置公用的部分,其他一概不要。)

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於我們</a>
    <a href="">預約電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">

</div>
</body>
</html>

接下來,我們將base menu1 menu2 menu3這四個頁面全部清空,然后在每個頁面的最上面加上這么一行代碼:

{% extends 'master_edition.html' %}

這個就實現了我要繼承母版master_edition.html。

3. 自定制效果

現在已經完成了繼承母版,這個只是減少了重復代碼,還沒有實現每個頁面自定制的一些內容,如果你想要實現自定制的內容怎么做?類似於模版系統你是不是應該在母版的具體位置做一個標識,然后在自己的頁面對應的地方進行自定制?那么這個類似於%占位符的特定的標識叫做鈎子。這幾個頁面只是在menu div不同,所以我們就在這里做一個鈎子就行了。

在母版的html對應的位置:

<div class="menu">
    {% block content %}
    {% endblock %}
</div>

block  endblock就是對應的鈎子,content是此鈎子的名字。

然后在base menu1 menu2 menu3的頁面上(此時就以base頁面舉例):

{% block content %}
     base頁面首頁
{% endblock %}

這樣你的代碼是不是非常的簡單了? 

那么我們不僅可以在對應的html標簽設置鈎子,還可以在css、js設定對應的鈎子。所以母版繼承中一般設定鈎子的地方就是三部分: html、css、js。

以css舉例:

我們將base頁面的頂端導航條的背景顏色設置成紅色:

首先現在母版頁面對應的位置設置鈎子:

 <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }
        {% block nav %}
        {% endblock %}
    </style>

然后找到base頁面:

{% block nav %}
    .nav{
    background-color: red;
    }
{% endblock %}

這樣你的base頁面的導航條就變成紅色啦!

js的母版繼承我們就不再這里嘗試了,自己私下可以嘗試一下。

4. 保留母版內容並添加新特性

還有一個情況我們也會遇到,就是我既要留住母版的內容,又要在自己的html添加一些新的標簽。這個我們在面向對象時是不是也遇到過?當時用什么方法既執行父類方法又可以執行子類方法?super!在這里我們也用super!

母版html:

<div class="menu">

    {% block content %}
        <div>這是母版測試頁面</div>
    {% endblock %}
</div>

base.html:

{% block content %}
    {{ block.super }}
     base頁面首頁
{% endblock %}

在鈎子里面加上{{ block.super }}即可。

5. 注意

  • 如果你在模版中使用 {% 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 the next section), since it was already escaped, if necessary, in the parent template.  將子頁面的內容和繼承的母版中block里面的內容同時保留。

  • 不能在一個模版中定義多個相同名字的 block 標簽。
  • 結束一個鈎子時可以標注此鈎子的名字,比如 {% endblock content%}這樣就可以結束此鈎子不至於將其他的block鈎子一並結束。

 六、組件

組件就是將一組常用的功能封裝起來,保存在單獨的html文件中,(如導航條,頁尾信息等)其他頁面需要此組功能時,按如下語法導入即可。 這個與繼承比較類似,但是‘格局’不同,繼承是需要寫一個大的母版,凡是繼承母版的一些html基本上用的都是母版頁面的布局,只有一部分是自己頁面單獨展現的,這好比多以及排布好的多個組件。

{% include 'xx.html' %}

比如我們寫一個nav導航條組件:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於我們</a>
    <a href="">預約電話</a>
    <input type="text">搜索
</div>



</body>
</html>

然后我們在創建流程去使用我們的組件:

urls:
url(r'^component/', views.component),


views:
def component(request):
    return render(request,'component.html')

component頁面:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
{% include 'nav.html' %}
<h1>你好,世界!</h1>
<div>我是componet頁面</div>


</body>
</html>

組件和插件的區別:

組件是提供某一完整功能的模塊,如:編輯器組件,QQ空間提供的關注組件 等。

而插件更傾向封閉某一功能方法的函數。

這兩者的區別在 Javascript 里區別很小,組件這個名詞用得不多,一般統稱插件。

 七 自定義標簽和過濾器

 我們都學過了Django給我們提供的標簽和過濾器,但是這些不能滿足我們日常開發的所有需求,所以DJango為我們提供了一個很牛逼的功能就是我們可以自定義標簽和過濾器。接下來我們看看這個怎么玩。 

1. 自定義過濾器

  Django的模版系統給我們提供了很多過濾器,比如切割、全大寫、獲取元素個數,聯合join,安全性質的safe等等,這些都是對數據(尤其是字符串類型的數據)進行加工,但是這些都是模版系統提供給我們的,不能滿足應對工作中的所有需求,所以Django給我們提供了口子:讓我們可以自己定義過濾器,這樣我們開發起來更加的靈活。接下來我們講解如何自己定義過濾器。

1. 在應用的目錄下創建templatetags文件夾(文件夾必須這樣命名)。

 

2. 在templatetags文件下創建py文件(任意命名即可)。

我這里命名為network_language.py。

 3. 在py文件中引入template模塊並創建注冊器。

from django import template


register = template.Library()

4. 自定義過濾器函數。

下面講到的自定義標簽以及simple_tags,與前三步一模一樣,並且都是在本文件中操作。

@register.filter
def adjoin(v1):
    pass

# adjoin就是我們自定義的過濾器函數。
# 此函數必須被register.filter裝飾,這樣才能生效。
# 我們現在先不做任何功能,一會在定義功能。

完成上面這個四步,我們就算是自定義了一個過濾器函數。那么如何使用呢?模版系統自帶的過濾器如何使用?他是通過對render要渲染的變量加上管道符,然后在管道符后面加上具體的過濾器的名字,這樣就可以對變量進行簡單加工了。我們自定義的過濾器也是這樣用法。接下來我們重新設計一個url流程:

urls:

    url(r'^templatetag/', views.template_tag),

views:

def template_tag(request):
    name = '天琪'
    return render(request,'templatetag.html', {'name': name})


html:
<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
{% load network_language %}  # 引入templatetags文件夾下面的network_language.py文件
{{ name|adjoin }}  # name就是render渲染的變量,adjoin就是自定義的過濾器的函數名

</body>
</html>

此時你自定義的過濾器里面的adjoin代碼:

from django import template


register = template.Library()


@register.filter
def adjoin(v1):
    return v1 + '噢里給'

  我們自定義的過濾器就是完成了簡單的功能:給name變量拼接了一個網絡用語:奧利給。那么整個他的執行流程是什么呢?

  首先瀏覽器發送請求至url,url通過請求路徑找到對應的views視圖函數template_tag,然后執行到return時先不會給瀏覽器馬上返回html頁面,而是通過render進行模版渲染,當渲染到 {% load network_language %}這一行,render會自動找到templatetags文件夾下面的network_language文件,然后在向下讀取,執行自定義過濾器adjoin函數,並且將變量name對應的'天琪'傳遞給adjoin函數的形參v1,得到返回值'天琪噢里給'之后,替換{{ name|adjoin }},渲染完畢之后,將此html頁面返回給瀏覽器。

  這個就是整體的過程。那么有人可能會問了,我自定義的過濾器函數只能設定一個形參么?不是的,自定的過濾器函數設定形式參數至多兩個。接下來我們演示一個兩個參數的。

# html:

{% load network_language %}
{{ name|_adjoin:'微笑的面對它' }}


# templatetags/network_language.py:

@register.filter
def _adjoin(v1, v2):
    return v1 + v2 + '噢里給!'

第二個參數是通過過濾器的冒號后面傳遞的。

至此,我們自定義過濾器這部分就已經給大家講完了,可以練習一下~

2. 自定義標簽

我們學習完自定義過濾器之后,再來看看自定義標簽,自定義標簽與自定義過濾器差不多,前三個步驟是一樣,接下來就是定義自定義標簽函數不同:

views:我們還是使用template_tag函數,只是增加了一些變量。

def template_tag(request):
    name = '天琪'
    content = '我太難了'
    status = '在線學習'
    return render(request,'templatetag.html', locals())

html:

{% load network_language %}
{% all_join name status content '不要怕'%}

network_language.py:

@register.simple_tag
def all_join(v1, v2, v3, v4):
    return v1 + v2 + v3 + v4 + '噢里給!'

其實自定義標簽和自定義過濾器差不多,都是我們設計一些函數,增加一些自定制的功能,也有一些細微的區別,我們總結一下:

  1. 自定制過濾器至多只能接受兩個參數,而自定制標簽可以接受多個參數,這樣看來自定制標簽更加靈活。
  2. 自定制過濾器必須依賴於 變量,他的目的就是對變量進行加工{{ name|adjoin }} ;而自定制標簽可以不依賴於變量直接使用{% all_join %}.
  3. 自定制過濾器可以用在if、for等語句后,自定制標簽不可以。
{% if num|filter_multi:30 > 100 %}
    {{ num|filter_multi:30 }}
{% endif %}

3.inclusion_tag

這個標簽不同於上面的兩種,這個標簽是將一段html代碼插入到我們html頁面中,有點兒類似於組件,但是比組件還靈活。上一節我們講組件時,我們將自己設計的一個頂端的導航條nav.html作為一個組件,然后讓另一個頁面去使用,還記得這個例子吧?這次我們用inclusion_tag去展示一下,對比分析一下這兩個有什么不同。

 

views:我們加了一個show_list變量

def template_tag(request):
    name = '天琪'
    content = '我太難了'
    status = '在線學習'
    show_list = ['課程分類', '在線學習', '解答疑問', '講師介紹', '網站介紹']
    return render(request,'templatetag.html', locals())

teamplatetag.html: 我設計在network_language中自定制inclusion標簽函數,函數名為nav_data,然后將show_list傳遞進去。

{% load network_language %}
{% nav_data show_list %}

network_language.py:

@register.inclusion_tag('nav.html')
def nav_data(argv):
    argv += [datetime.datetime.now()]return {'data': argv}

此時我在這個自定制函數中對show_list進行了加工,並且通過return(這里的return有點兒類似於render)將數據傳遞到nav.html頁面對應的位置。

nav.html:

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }

    </style>

</head>

<body>

<div class="nav clearfix">
    {% for foo in data %}
        <a href="">{{ foo }}</a>
    {% endfor %}
    <input type="text">搜索
</div>



</body>
</html>

然后將數據展示到導航條上面相應的位置上,最終的效果如下:

然后我們對比分析組件,組件是只能將html里面的一部分(導航條、左右側欄等)原封不動被引入到相應的其他html頁面中,而

inclusion_tag這個自定制標簽,不僅可以完成組件的功能,而且還可以操作被導入的html組件,使其更加靈活。

 八、靜態文件

截止到目前為止,我們還沒有應用到css、js等文件,只是使用了html頁面,現在我們的頁面可算是不要太丑了。所以項目中我們肯定是要引入靜態文件的,什么是靜態文件?靜態文件就是我們之前學過的css、js、圖片文件、視頻文件等等,我們在html想要使用他們就是引用靜態文件,之前我們寫頁面是需要通過標簽引入靜態文件,並且需要各種路徑的配置,現在我們利用Django做一個簡單的配置就可以直接引入靜態文件了。

我們在重新創建一個項目static_pro:

url:
url(r'^index/', views.index),

views:
def index(request):
    return render(request, 'index.html')

html:
<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>
</html> 

  我們的靜態文件不只是一個文件,這里面包含很多的文件,所以我們應該創建一個文件夾,將所有的靜態文件都放置在此目錄中,所以,我們在整個項目的目錄下創建一個專門放置靜態文件的文件夾:我們取名'jingtaiwenjian'(為了教學使用,工作中決不允許出現拼音形式),然后我們在此靜態文件里面創建一個css1.css文件,專門放置css代碼。

 

接下來我們如何引用我們的css1靜態文件呢?按照我們之前的操作,在html頁面的link標簽中引入此文件的相對路徑,是行不通的。

引入靜態文件方式一

我們在Django框架中,就遵循Django規范,那么Django是如何配置css。

1. 在settings配置文件中,配置靜態文件路徑

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'jingtaiwenjian'),
] 
#
STATICFILES_DIRS 此常量名設置可以任意構建,一般約定俗成都是這樣命名的。

2. 在index.html文件中,引入css文件

<link href="/static/css1.css" rel="stylesheet">

注意:這里面的路徑如果你寫的是/jingtaiwenjian/css1.css,這樣css文件是引用不到的,你要像我上面寫的一樣。那么為什么呢?這個'static'是什么東西?接下來跟你簡單聊聊這個static。

別名static

  這個static是給靜態文件(比我們的項目的靜態文件的名字為:jingtaiwenjian)起的別名,他在哪里可以配置呢?在我們settings里面就可以配置:

 

 Django默認給靜態文件起了別名,當然你也可以自己設定別名,比如:

STATIC_URL = '/olddriver/'

如果你要是更改這個別名,你在index.html中的引用就需要更改,所以一般我們不去更改別名:

這樣也可以成功訪問,那么Django為什么要給靜態文件起別名呢?這樣有什么好處呢?

  1. 使用別名代替真實的靜態文件名字,防止有人對你的靜態文件發起惡意攻擊,保證靜態文件的安全。

    因為請求一個web網站時,都會對css、js發起請求,並且這些請求都是可以訪問到的url:

    

 

     我們訪問一下這個URL:

    

    再比如我們打開京東,看一下京東的網絡請求:

    

 

 

     通過這個網址我們可以拿到他的css代碼:

    

 

 

    這種css代碼是壓縮混淆處理的,有些內容你是看不懂的,就相當於加密了,但是我們可以獲取到。雖然可以讓你獲取到,但是由於你的static是別名,他是不能對你的靜態文件發起攻擊的。

  2. 使用別名代替真實的靜態文件名字,保持了代碼的拓展性。

    如果以后遇到了需求,必須將靜態文件的名字改掉(比如我們本次取的名字jingtaiwenjian就不規范),如果沒有別名的話,你必須將所有的引用靜態文件的相關link路徑都必須改掉,這樣太麻煩了,但是我們有了別名的概念,無論你以后如何更改靜態文件的名字,都不會造成影響的。

    

我將靜態文件名字改成了staticfile,當我再次訪問index頁面時,依然還是可以訪問的。

 

請求路徑還是別名作為路徑。

所以,針對於上述情況,起別名還是又好處的。

引入靜態文件方式二

  我們還可以通過第二種方式引入靜態文件,第一種方式是有漏洞的,假如我們已經完成了一期項目,你的項目中已經創建了多個html文件,並且都一直通過第一種方式引入靜態文件,此時你跳槽了,其他的人接收你的項目,他沒有注意到你的別名為static,而當它二期項目已經進展了很長時間發現你的static別名與二期項目中的某些url沖突了,這時必須要更改你的別名才可以解決問題,那么怎么做?因為你通過第一種方式引入的靜態文件,如果你把別名改了,你的很多html頁面的link路徑都必須要更改,所以這種方式還是有弊端的。那么接下來我介紹第二種引入靜態文件的方式,就可以完美避開這個問題。

settings:

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'staticfile'),
]

index.html:

{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="{% static 'css1.css' %}" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>
</html>

通過這種方式引入靜態文件,無論你更改別名,都沒有影響,因為他是根據settings你配置的靜態文件的路徑找到你的靜態文件的,不依賴於別名,所以這種方式也是我比較喜歡的一種方式。

了解:如果某個靜態文件路徑過長(比如一個圖片)並且此圖片多次被用到,我們每次引用這個圖片寫路徑很麻煩時,這樣可以給這個圖片路徑起個別名,以后在引用起來就簡單了:

{% load static %}
{% static 'css1.css' as c %}
<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="{% static 'css1.css' %}" rel="stylesheet">
    <link href="{{ c }}" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>

引入靜態文件方式三

我們也可以通過 {% get_static_prefix %}方式引入靜態文件,這樣你的文件分隔符可以省略不寫了。

{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
    <link href="{% get_static_prefix %}css1.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>
</html>

了解:如果某個靜態文件路徑過長(比如一個圖片)並且此圖片多次被用到,我們每次引用這個圖片寫路徑很麻煩時,這樣可以給這個圖片路徑起個別名,以后在引用起來就簡單了:

{% load static %}
{% get_static_prefix as c %}

<!DOCTYPE html>
<html lang="zh-CN">
<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>Bootstrap 101 Template</title>
{#    <link href="{% static 'css1.css' %}" rel="stylesheet">#}
{#    <link href="{{ c }}" rel="stylesheet">#}
    <link href="{{ c }}css1.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>
</html>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM