19 Django模板自定義標籤和過濾器,模板繼承 (extend),Django的模型層-ORM簡介
一、模板自定義標籤和過濾器
標籤,是為了做一些功能。過濾器,是對斜槓前面的資料做過濾。
為什麼要自定義標籤和過濾器?因為自帶的不夠用,需要結合需求,來自定義。
自定義標籤和過濾器需要執行3個步驟:
1、在settings中的INSTALLED_APPS配置當前app,不然django無法找到自定義的simple_tag. 2、在app中建立templatetags模組(模組名只能是templatetags) 3、建立任意 .py 檔案,如:my_tags.py
自定義過濾器
舉例:增加一個乘法過濾器
修改settings.py中的INSTALLED_APPS,最後一行添加當前的app。
django開頭的,都是一些自帶的app。它內建在django原始碼裡面!
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', ]View Code
在app01目錄裡面新建一個templatetags目錄,目錄名必須是這個!!!否則django找不到。
目錄裡面建立my_filter_tag.py,這個py檔名,可以隨便定義。內容如下:
from django import template from django.utils.safestring import mark_safe register=template.Library() @register.filter def multi_filter(x,y): return x*yView Code
注意:頭部的3行,是固定寫法,不能改變。
增加@register.filter,是為了將函式轉換成過濾器。函式的名字,可以自定義。
修改views.py裡面的index函式,內容如下:
def index(request): num = 100 return render(request,'index.html',{'num':num})View Code
修改index.html,修改body部分
注意:在使用自定義標籤和過濾器,必須在html檔案中,匯入之前建立的my_filter_tag
{% load my_filter_tag %}
<p>{{ num|multi_filter:5 }}</p>
View Code
load表示匯入模組。p標籤中的內容,是執行了multi_filter過濾器。
注意:它接收了2個引數。一個是num,一個是5。因為multi_filter過濾器,定義了2個形參,使用它必須傳2個引數才行。
訪問index頁面,輸出:
是因為自定義的模組,沒有匯入成功,提示找不到!為啥呢?因為django專案啟動時,會匯入settings.py定義的模組匯入,由於app01的自定義模組是啟動之後加的,所以它並沒有載入進去。
重啟django專案,就可以載入了。
再次訪問index,頁面輸出:
如果要完成3位乘法呢?過濾器可以增加一個形參,但是index.html怎麼加第3個引數呢?
答案是,它不能加第3個引數。所以只能在後端,將引數形式修改列表,元組,字典等方式,就可以了。
舉例1:計算4*5*6
修改index檢視函式
def index(request): num1 = 4 num2 = 5 num3 = 6 num_list = [num1,num2,num3] return render(request,'index.html',{'num_list':num_list})View Code
修改my_filter_tag.py中的過濾器
@register.filter def multi_filter(num_list): res = 1 for i in num_list: res*=i return resView Code
修改index.html,修改body部分
{% load my_filter_tag %}
<p>{{ num_list|multi_filter }}</p>
View Code
訪問網頁,輸出:
舉例1:顯示a標籤
修改my_filter_tag.py檔案,增加link_tag過濾器
@register.filter def link_tag(href): return "<a href=%s>click</a>"%hrefView Code
修改index檢視函式
def index(request): link = "http://www.py3study.com/" return render(request,'index.html',{'link':link})View Code
修改index.html,修改body部分
{% load my_filter_tag %}
<p>{{ link|link_tag }}</p>
View Code
訪問網頁,輸出:
發現結果不是我們想要的,檢視瀏覽器控制檯,檢視響應體,發現被轉義了
因為django遇到html或者js標籤,會轉義。它認為是不安全的!那麼如何告訴它,是安全的呢?
需要在過濾器中匯入make_safe模組
修改my_filter_tag.py檔案中的link_tag過濾器,完整程式碼如下:
使用make_safe方法,告訴django是安全的,不需要轉義
from django import template from django.utils.safestring import mark_safe register=template.Library() @register.filter def multi_filter(num_list): res = 1 for i in num_list: res*=i return res @register.filter def link_tag(href): return mark_safe("<a href=%s>click</a>" % href)View Code
重啟django專案,因為網頁有快取,懶得清理了,所以直接重啟django專案,見效快!
再次訪問
效果就出來了。
自定義標籤
標籤,是為了做一些功能
舉例:4個引數的乘法運算
修改my_filter_tag.py,增加multi_tag函式
@register.simple_tag def multi_tag(x,y,z): return x*y*zView Code
@register.simple_tag表示將函式轉換為自定義標籤
修改index.html,修改body部分
注意:呼叫標籤,使用{% 標籤過濾器名 引數1,引數2,引數3... %}
引數不限,但不能放在if for語句中
{% load my_filter_tag %}
<p>{% multi_tag 7 8 9 %}</p>
View Code
重啟django專案,訪問網頁,輸出:
自定義標籤和自定義過濾器的區別:
1. 標籤,是為了做一些功能。過濾器,是對斜槓前面的資料做過濾。
2. 標籤可以寫任意個形參,而過濾器最大隻能寫2個形參。如果過濾器需要接收多個引數,需要將引數存放在列表,元組,字典等資料中。
3. 過濾器可以用在if等語句後,標籤不可以
二、模板繼承 (extend)
Django模版引擎中最強大也是最複雜的部分就是模版繼承了。模版繼承可以讓您建立一個基本的“骨架”模版,它包含您站點中的全部元素,並且可以定義能夠被子模版覆蓋的 blocks 。
通過從下面的例子開始,可以容易的理解模版繼承:
include
新建advertise.html,使用Bootstrap構建2個面板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> </head> <body> <div class="row"> <div class="col-md-4"> <div class="advertise"> <div class="panel panel-danger"> <div class="panel-heading">選單一</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading">選單一</div> <div class="panel-body"> Panel content </div> </div> </div> </div> </div> </body> </html>View Code
修改urls.py,增加advertise路徑
urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index), path('advertise/', views.advertise), ]View Code
修改views.py,增加advertise檢視函式
def advertise(request): return render(request, 'advertise.html')View Code
訪問url: http://127.0.0.1:8000/advertise/
如果首頁,也想顯示左邊的選單欄呢?程式碼複製一遍?
這樣程式碼就重複了,使用include,就可以了。
修改advertise.html,將相同的部分分離處理
<div class="row"> <div class="col-md-4"> <div class="advertise"> <div class="panel panel-danger"> <div class="panel-heading">選單一</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading">選單一</div> <div class="panel-body"> Panel content </div> </div> </div> </div> </div>View Code
修改index.html,使用include匯入advertise.html檔案
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> </head> <body> {% include 'advertise.html' %} </body> </html>View Code
訪問首頁,效果同上。
extend
訪問部落格園的個人部落格,發現很多頁面,有幾處相同的部分。比如右側的側邊欄,頂部的導航欄,尾部部分...等等。
那麼公共區域,不需要自己重複寫,繼承下來就可以了!
舉例:圖書網頁
編輯index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <style> * { margin: 0; padding: 0; } .header { width: 100%; height: 60px; background-color: #369; } .title { line-height: 60px; color: white; font-weight: 100; margin-left: 20px; font-size: 20px; } .container{ margin-top: 20px; } </style> </head> <body> <div class="header"> <p class="title"> 路飛學誠 </p> </div> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="panel panel-danger"> <div class="panel-heading">選單一</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading">選單二</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-warning"> <div class="panel-heading">選單三</div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-9"> <div class="jumbotron"> <h1>Hello, INDEX!</h1> <p>...</p> <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p> </div> </div> </div> </div> </body> </html>View Code
訪問index頁面,輸出:
urlpatterns = [ path('admin/', admin.site.urls), path('index/',views.index), path('articles/',views.articles), path('authors/',views.authors), ]View Code
修改views.py,增加2個檢視函式
def articles(request): return render(request, 'articles.html') def authors(request): return render(request, 'authors.html')View Code
新建articles.html,將index.html的程式碼全部複製貼上過來,修改中間內容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <style> * { margin: 0; padding: 0; } .header { width: 100%; height: 60px; background-color: #369; } .title { line-height: 60px; color: white; font-weight: 100; margin-left: 20px; font-size: 20px; } .container{ margin-top: 20px; } </style> </head> <body> <div class="header"> <p class="title"> 路飛學誠 </p> </div> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="panel panel-danger"> <div class="panel-heading">選單一</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading">選單二</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-warning"> <div class="panel-heading">選單三</div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-9"> <div class="article_list"> <ul> <li>紅樓夢</li> <li>三國演義</li> <li>西遊記</li> <li>水滸傳</li> </ul> </div> </div> </div> </div> </body> </html>View Code
訪問articles頁面,效果如下:
新建authors.html,將index.html的程式碼全部複製貼上過來,修改中間內容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <style> * { margin: 0; padding: 0; } .header { width: 100%; height: 60px; background-color: #369; } .title { line-height: 60px; color: white; font-weight: 100; margin-left: 20px; font-size: 20px; } .container{ margin-top: 20px; } </style> </head> <body> <div class="header"> <p class="title"> 路飛學誠 </p> </div> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="panel panel-danger"> <div class="panel-heading">選單一</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading">選單二</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-warning"> <div class="panel-heading">選單三</div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-9"> <div class="author_list"> <ul> <li>曹雪芹</li> <li>羅貫中</li> <li>吳承恩</li> <li>施耐庵</li> </ul> </div> </div> </div> </div> </body> </html>View Code
訪問authors頁面,效果如下:
問題來了,選單和導航,都是重複的。有了繼承,就可以解決這個問題。
公共部分有,繼承的子頁面就會有。沒有的部分,子頁面也不會有。
這個模版,我們把它叫作 base.html, 它定義了一個可以用於兩列排版頁面的簡單HTML骨架。“子模版”的工作是用它們的內容填充空的blocks。
在這個例子中, block 標籤定義了三個可以被子模版內容填充的block。 block 告訴模版引擎: 子模版可能會覆蓋掉模版中的這些位置。
子模版可能看起來是這樣的:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style> * { margin: 0; padding: 0; } .header { width: 100%; height: 60px; background-color: #369; } .title { line-height: 60px; color: white; font-weight: 100; margin-left: 20px; font-size: 20px; } .container{ margin-top: 20px; } </style> </head> <body> <div class="header"> <p class="title"> 路飛學誠 </p> </div> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="panel panel-danger"> <div class="panel-heading"><a href="http://127.0.0.1:8008/index/">首頁</a></div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading"><a href="http://127.0.0.1:8008/authors/">作者</a></div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-warning"> <div class="panel-heading"><a href="http://127.0.0.1:8008/articles/">文章管理</a></div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-9"> {% block content %} {% endblock %} </div> </div> </div> </body> </html>View Code
修改index.html,刪除多餘的程式碼。繼承base.html
它是先繼承,再填充內容。
extends 標籤是這裡的關鍵。它告訴模版引擎,這個模版“繼承”了另一個模版。當模版系統處理這個模版時,首先,它將定位父模版——在此例中,就是“base.html”。
那時,模版引擎將注意到 base.html 中的三個 block 標籤,並用子模版中的內容來替換這些block。根據 blog_entries 的值,輸出可能看起來是這樣的:
{% extends 'base.html' %} {% block content %} <div class="jumbotron"> <h1>Hello, INDEX!</h1> <p>...</p> <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p> </div> {% endblock %}View Code
訪問index頁面,效果如下:
修改articles.html,繼承base.html
{% extends 'base.html' %} {% block content %} <div class="article_list"> <ul> <li>紅樓夢</li> <li>三國演義</li> <li>西遊記</li> <li>水滸傳</li> </ul> </div> {% endblock %}View Code
訪問articles頁面,效果如下:
修改authors.html,繼承base.html
{% extends 'base.html' %} {% block content %} <div class="author_list"> <ul> <li>曹雪芹</li> <li>羅貫中</li> <li>吳承恩</li> <li>施耐庵</li> </ul> </div> {% endblock %}View Code
訪問authors頁面,效果如下:
這樣,就解決了程式碼重複問題
需求,訪問不同頁面時,更改tile標籤的屬性
怎麼做呢?很簡單,在base.html裡面的title標籤位置,定義一個block
在花括號中間的title標籤,表示預設值。如果子頁面不設定block title,那麼顯示預設值。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {% block title %} <title>title</title> {% endblock title %} <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style> * { margin: 0; padding: 0; } .header { width: 100%; height: 60px; background-color: #369; } .title { line-height: 60px; color: white; font-weight: 100; margin-left: 20px; font-size: 20px; } .container{ margin-top: 20px; } </style> </head> <body> <div class="header"> <p class="title"> 路飛學誠 </p> </div> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="panel panel-danger"> <div class="panel-heading"><a href="http://127.0.0.1:8008/index/">首頁</a></div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading"><a href="http://127.0.0.1:8008/authors/">作者</a></div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-warning"> <div class="panel-heading"><a href="http://127.0.0.1:8008/articles/">文章管理</a></div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-9"> {% block content %} {% endblock %} </div> </div> </div> </body> </html>View Code
修改articles.html,增加一個block
{% extends 'base.html' %} {% block content %} {{ block.super }} <div class="article_list"> <ul> <li>紅樓夢</li> <li>三國演義</li> <li>西遊記</li> <li>水滸傳</li> </ul> </div> {% endblock %} {% block title %} <title>Article</title> {% endblock %}View Code
訪問articles頁面,標題就出來了,效果如下:
訪問authors頁面,標題就出來了,效果如下:
由於沒有設定block title,它顯示預設值。
現在有一個問題,右側內容,寫死了。比如文章頁中的內容
修改views.py,2個檢視,分別增加一個列表
def articles(request): acticle_list = ['紅樓夢','三國演義','西遊記','水滸傳'] return render(request, 'articles.html',{'acticle_list':acticle_list}) def authors(request): author_list = ['曹雪芹','羅貫中','吳承恩','施耐庵'] return render(request, 'authors.html',{'author_list':author_list})View Code
修改修改articles.html,增加一個for迴圈
{% extends 'base.html' %} {% block content %} <div class="article_list"> <ul> {% for article in acticle_list %} <li>{{ article }}</li> {% endfor %} </ul> </div> {% endblock %} {% block title %} <title>Article</title> {% endblock %}View Code
修改修改authors.html,增加一個for迴圈
{% extends 'base.html' %} {% block content %} <div class="author_list"> <ul> {% for author in author_list %} <li>{{ author }}</li> {% endfor %} </ul> </div> {% endblock %} {% block title %} <title>Author</title> {% endblock %}View Code
重新訪問2個頁面,效果同上。
請注意,子模版並沒有定義 sidebar block,所以系統使用了父模版中的值。父模版的 {% block %} 標籤中的內容總是被用作備選內容(fallback)。
這種方式使程式碼得到最大程度的複用,並且使得新增內容到共享的內容區域更加簡單,例如,部分範圍內的導航。
這裡是使用繼承的一些提示:
-
如果你在模版中使用
{% extends %}
標籤,它必須是模版中的第一個標籤。其他的任何情況下,模版繼承都將無法工作。
舉例:下面這種寫法是錯誤的。extends必須在block上面才行。
{% block content %} <div class="jumbotron"> <h1>Hello, INDEX!</h1> <p>...</p> <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p> </div> {% endblock %} {% extends 'base.html' %}View Code
訪問網頁,就會報錯
-
在base模版中設定越多的
{% block %}
標籤越好。請記住,子模版不必定義全部父模版中的blocks,所以,你可以在大多數blocks中填充合理的預設內容,然後,只定義你需要的那一個。多一點鉤子總比少一點好。
比如:base.html雖然定義了2個blocks,authors頁面可以只繼承一個。
-
如果你發現你自己在大量的模版中複製內容,那可能意味著你應該把內容移動到父模版中的一個
{% 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.super}}變數將執行該操作。如果你想新增父塊的內容而不是完全覆蓋父塊的內容,這很有用。使用{{block.super}}插入的資料不會自動轉義(請參閱下一節),因為它已經在父模板中被轉義(如有必要)。
舉例:
在base.html中block content裡面增加一個h3標籤。既系統子頁面顯示h3標籤,同時子標籤填充block content裡面的內容時,h3標籤不會被覆蓋。加入一個{{block.super}}就可以了。
修改base.html,加入h3標籤。修改block content部分
{% block content %} <h3>詳細內容</h3> {% endblock %}
修改articles.html,在block content 下面一行,增加{{block.super}}
{% extends 'base.html' %} {% block content %} {{ block.super }} <div class="article_list"> <ul> {% for article in acticle_list %} <li>{{ article }}</li> {% endfor %} </ul> </div> {% endblock %} {% block title %} <title>Article</title> {% endblock %}View Code
訪問articles頁面,效果如下
注意