1. 程式人生 > >19 Django模板自定義標籤和過濾器,模板繼承 (extend),Django的模型層-ORM簡介

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*y
View 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 res
View 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>"%href
View 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*z
View 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頁面,效果如下

注意