5、Django的模板渲染和繼承

Django的模板渲染,繼承css

1、語法html

兩種特殊符號:django

{{ }} 和 {% %}後端

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

2、變量緩存

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

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

urls.py 先配置路徑:
    url(r'^index/', views.index),
    
views.py 
    from django.shortcuts import render,HttpResponse,redirect
    
    def index(request):
        num = 100
        name = 'alex'
        food_list = ['蒸熊掌','燒花鴨','燒子鵝']
        dic = {'year':2019,'month':7,'day':17}

        class Animal:
            def __init__(self):
                self.kind = 'dog'
            def color(self):
                return 'black'
        a = Animal()

        return render(request,'index.html',{'num':num,'name':name,'food_list':food_list,'dic':dic,'a':a})
        # 經過字典的形式傳把值給html文件
        return render(request,'index.html',locals())
        # locals() 獲取函數內部全部變量的值,並加工成{'變量名':'變量值'....}這樣一個字典

index.html
    <p>{{ num }}</p>
    <p>{{ name }}</p>
    <p>{{ food_list }}</p>
    <p>{{ dic }}</p>
    <p>{{ a }}</p>
    <p>{{ a.kind }}</p>
    <p>{{ a.color }}</p>

注意:xss

  • views中:對於locals()這種方法,能夠獲取函數內部全部變量的值,圖省事,能夠用它,可是不少多餘的變量也被傳進去了,效率低
  • html中:調用對象裏面的方法的時候,不須要寫括號來執行;而且只能執行不須要傳參數的方法,若是這個方法須要傳參數,那麼模板語言不支持,不能幫你渲染

3、過濾器編輯器

在Django的模板語言中,經過使用 過濾器 來改變變量的顯示。

過濾器的語法: {{ value|方法名:參數 }} 使用管道符"|"來應用過濾器 ,冒號":"來鏈接參數

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

length

獲取數據長度,沒參數,做用於字符串和列表   {{ value|length }} 

<p>{{ food_list|length }}</p>

default

默認值,有參數,若是一個變量是false或者爲空,使用給定的默認值。 不然,使用變量的值

<p>{{ xx|default:'啥也沒有' }}</p>  # views若沒有傳過來xx的值,因此就會顯示設置的默認值

filesizeformat

將值格式化爲一個 「人類可讀的」 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)

# 好比在views中  size = 123456 
<p>{{ size|filesizeformat }}</p>  # 此時格式轉換成  120.6 KB

slice

切片 ,跟字符串、列表的切片方法同樣   {{value|slice:"2:-1"}}

<p>{{ name|slice:':3' }}</p>  # 0可省略

date

時間格式化顯示

# 好比在views中  ntime = datetime.datetime.now()
<p>{{ ntime|date }}</p>                # July 17, 2019
<p>{{ ntime|date:"Y-m-d H:i:s"}}</p>   # 2019-07-17 16:27:55

truncatechars

字符截斷  若是字符串字符多於指定的字符數量,那麼會被截斷。截斷的字符串將以可翻譯的省略號序列(「...」)結尾。 參數:截斷的字符數 

# 好比在views中  name = 'alexdsb'
<p>{{ name|truncatechars:7 }}</p>    # alex...
#注意:最後那三個省略號也是7個字符裏面的,也就是這個7截斷出來的是4個字符+3個省略號

truncatewords

單詞截斷  在必定數量的字後截斷字符串,是截多少個單詞 

# 好比在views中  word = 'hello girl hi baby yue ma'
<p>{{ word|truncatewords:4 }}</p>  # hello girl hi baby ...
# 單詞截斷,截的是多少個單詞,省略號不算

cut

移除value中全部的與給出的變量相同的字符串  

# 好比在views中  word = 'hello girl hi baby yue ma'
<p>{{ words|cut:'i' }}</p>   # hello grl h baby yue ma

join

使用字符串鏈接列表,{{ list|join:', ' }},就像Python的str.join(list)  

# 好比在views中  food_list = ['蒸熊掌','燒花鴨','燒子鵝']
<p>{{ food_list|join:'+' }}</p>   # 蒸熊掌+燒花鴨+燒子鵝

safe

將 字符串識別成標籤

假如在用戶評論的時候寫了一段js代碼,若是不轉義,js代碼就會執行了,瀏覽器會一直彈窗等等,這叫作xss攻擊。Django的模板中在進行模板渲染的時候,爲了安全,會對HTML標籤和JS等語法標籤進行自動轉義 。在Django中關閉HTML的自動轉義有兩種方式,若是是一個單獨的變量咱們能夠經過過濾器「|safe」的方式告訴Django這段代碼是安全的沒必要轉義。

# 好比在views中  tag = '<a href="http://www.baidu.com">百度</a>'

<p>{{ tag }}</p>      # <a href="http://www.baidu.com">百度</a> 只會當成字符串
<p>{{ tag|safe }}</p> # 顯示了一個百度的a標籤

4、標籤Tags

標籤看起來像是這樣的: {% tag %}

經過循環或邏輯來控制流程,一些加載其後的變量將使用到的額外信息到模版中。

一些標籤須要開始和結束標籤 (例如{% tag %} ...標籤 內容 ... {% endtag %})。

for循環標籤

for循環普通的方法

循環一個字典:

views
    dic = {'year':2019,'month':7,'day':17}
html
    {% for key,value in dic.items %}
        <li>{{ key }} -- {{ value }}</li>
    {% endfor %}
   

循環一個列表:
views
    food_list = ['蒸熊掌','燒花鴨','燒子鵝']
html
    <ul>
        {% for food in food_list %}
            <li>{{ food }}</li>
        {% endfor %}
    </ul>
    
    # 利用 reversed 反向完成循環
    <ul>
        {% for food in food_list reversed %}
            <li>{{ food }}</li>
        {% endfor %}
    </ul>

for循環其餘方法

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

注意:循環序號能夠經過{{forloop}}顯示,必須在循環內部用 

{% for key,value in dic.items %}
    {{ forloop.counter }}             # 從1開始計數,好比dic有3個鍵值對,就1,2,3
    <li>{{ key }} -- {{ value }}</li>
{% endfor %}

{% for key,value in dic.items %}
    {{ forloop.counter0 }}             # 從0開始計數,好比dic有3個鍵值對,就0,1,2
    <li>{{ key }} -- {{ value }}</li>
{% endfor %}

{% for key,value in dic.items %}
    {{ forloop.revcounter }}            # 倒序直到1計數,好比dic有3個鍵值對,就3,2,1
    <li>{{ key }} -- {{ value }}</li>
{% endfor %}

{% for key,value in dic.items %}
    {{ forloop.revcounter0 }}            # 倒序直到0計數,好比dic有3個鍵值對,就2,1,0
    <li>{{ key }} -- {{ value }}</li>
{% endfor %}

{% for key,value in dic.items %}
    {{ forloop.first }}                  # 判斷是否是第一次循環,顯示bool值
    <li>{{ key }} -- {{ value }}</li>
{% endfor %}

{% for key,value in dic.items %}
    {{ forloop.last }}                    # 判斷是否是第一次循環,顯示bool值
    <li>{{ key }} -- {{ value }}</li>
{% endfor %}


forloop.parentloop示例:
循環views中的列表  d1 = [['春天','夏天','秋天','冬天'],['雨水','清明']]

{% for dd1 in d1 %}
    {% for ddd1 in dd1 %}
        {{ forloop.parentloop.counter }}
        {{ forloop.counter }}
        <div>{{ ddd1 }}</div>
    {% endfor %}
{% endfor %}

1 1  父輩的循環計數是1,本身的循環計數是1
春天
1 2  父輩的循環計數是1,本身的循環計數是2
夏天
1 3  父輩的循環計數是1,本身的循環計數是3
秋天
1 4  父輩的循環計數是1,本身的循環計數是4
冬天
2 1  父輩的循環計數是2,本身的循環計數是1
雨水
2 2  父輩的循環計數是2,本身的循環計數是2
清明

for .... empty

for 標籤帶有一個可選的{% empty %} 從句,以便在給出的組是空的或者沒有被找到時,能夠有所操做。

好比在views中並無給出 person_list,此時html中

{% for person in person_list %}
    <p>{{ person.name }}</p>

{% empty %}
    <p>sorry,no person here</p>
{% endfor %}

由於沒有找到這個列表,頁面就會顯示empty中的內容 sorry,no person here

若是有  person_list = [{'name':'dazhuang'},{'name':'taibai'},{'name':'alex'}]
此時頁面就會顯示:dazhuang、taibai、alex

if標籤

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

注意:

  • Django的模板語言不支持連續判斷,即不支持如下寫法:
    {% if a > b > c %}
    ...
    {% endif %}
  • 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的值,即取d的items鍵對應的值100,而不是執行items方法

    {% if num > 100 or num < 0 %}

    什麼鬼


    {% elif num >= 80 and num <= 100 %}

    大佬真猛


    {% else %}

    湊活吧


    {% endif %}

    假如views中傳過來 num = 100,則頁面顯示 大佬真猛

with標籤

使用一個簡單地名字緩存一個複雜的變量,多用於給一個複雜的變量起別名,注意等號左右不要加空格

假如views中有這樣一個列表 person_list = [{'name':'dazhuang'},{'name':'taibai'},{'name':'alex'}],我想把 alex 取出來

兩種方式:
    方式一:
        {% with name=person_list.2.name %}  # 注意等號兩邊沒有空格
            {{ name }}        # 只能在with語句體內用,外邊就很差使了
        {% endwith %}
    方式二:
        {% with person_list.2.name as name %}
            {{ name }}
        {% endwith %}

csrf_token標籤

當以post提交表單的時候,會報錯,咱們以前的解決辦法是在settings裏面的中間件配置裏面把一個csrf的防護機制給註銷了 ,而自己是不該該註銷的,不讓本身的操做被forbiden,經過csrf_token標籤就能搞定。

這個標籤用於跨站請求僞造保護  

在頁面的form表單裏面(注意是在form表單裏面)任何位置寫上{% csrf_token %},這個東西模板渲染的時候替換成了<input type="hidden" name="csrfmiddlewaretoken" value="8J4z1wiUEXt0gJSN59dLMnktrXFW0hv7m4d40Mtl37D7vJZfrxLir9L3jSTDjtG8">,隱藏的,這個標籤的值是個隨機字符串,提交的時候,這個東西也被提交了,首先這個東西是咱們後端渲染的時候給頁面加上的,那麼當你經過我給你的form表單提交數據的時候,你帶着這個內容我就認識你,不帶着,我就禁止你,由於後臺咱們django也存着這個東西,和你這個值相同的一個值,能夠作對應驗證是否是我給你的token,就像一個咱們後臺給這個用戶的一個通行證,若是你用戶沒有按照我給你的這個正常的頁面來post提交表單數據,或者說你沒有先去請求我這個登錄頁面,而是直接模擬請求來提交數據,那麼我就能知道,你這個請求是非法的。

好比:

views:
    def index(request):
    if request.method == 'GET':
        return render(request,'index.html')
    else:
        return HttpResponse('成功了呢!!!')
    
index.html:
    <form action="" method="post"> 
        內容:<input type="text">
        <button>提交</button>
    </form>
    
# 此時,咱們沒有加{% csrf_token %},也沒註銷settings裏的防護機制,則網頁會報錯操做被forbiden

加上 {% csrf_token %} 試一試:

views:
    def index(request):
    if request.method == 'GET':
        return render(request,'index.html')
    else:
        return HttpResponse('成功了呢!!!')
    
index.html:
    <form action="" method="post"> 
        {% csrf_token %}
        內容:<input type="text">
        <button>提交</button>
    </form>
    
# 此時,咱們就能夠正常的拿到post請求後執行的頁面了

5、模板繼承

語法: {% extends 'base.html' %} ,可預留鉤子block

模版繼承就是建立一個基本的「骨架」模版,它包含站點中的所有元素,而且能夠定義可以被子模版覆蓋的 blocks 。

當咱們寫許多網頁的時候,發現部分網頁裏面的許多內容都很相似,這樣咱們就把類似的內容都拿出來,製做成一個母版,須要的頁面來繼承,這樣就會節省許多的重複代碼。 



製做這樣一個界面,輸入127.0.0.1:8000/home/,訪問首頁,顯示右邊內容是home頁面;點擊菜單1時,跳轉到menu1界面,右邊顯示menu1;菜單2和3同理;
  1. 先配置路徑 urls:
    from django.conf.urls import url
    from app01 import views

    urlpatterns = [
        url(r'^home/', views.home),
        url(r'^menu1/', views.menu1),
        url(r'^menu2/', views.menu2),
        url(r'^menu3/', views.menu3),
    ]
    配置好首頁路徑home/,和三個菜單的路徑
  2. 視圖函數 views:
    from django.shortcuts import render

    def home(request):
        return render(request,'home.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')
    寫首頁和三個菜單的視圖函數
  3. 咱們須要創建四個html文件:一個首頁的home.html,和三個菜單的html
    當咱們不用模板繼承的方法時,由於導航欄和菜單欄四個文件都是同樣的,因此四個文件的代碼大部分同樣,這樣的重複代碼就過多了
    以首頁 home爲例,三個菜單的基本同樣

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <title>模板繼承</title>
        <style>
            body{
                margin: 0;
                padding: 0;
            }
            .nav{
                background-color: black;
                height: 40px;
                line-height: 40px;
            }
            .nav a{
                color: white;
                text-decoration: none;
                padding: 0 50px;
            }
            .left-menu{
                background-color: gray;
                color: white;
                height: 600px;
                width: 20%;
                float: left;
            }
            .left-menu a{
                color: white;
                text-decoration: none;
            }
            ul{
                margin: 0;
                padding: 0;
            }
            li{
                padding-left: 75px;
                height: 50px;
                line-height: 50px;
                border: 1px solid black;
            }
            .content{
                width: 80%;
                float: right;
            }
            .clearfix{
                content: '';
                display: block;
                clear: both;
            }
        </style>
    
    
    </head>
    <body>
    
    <div class="nav">
        <a href="">外設首頁</a>
        <a href="">鼠標專賣</a>
        <a href="">鍵盤專賣</a>
        <a href="">耳麥專賣</a>
        <input type="text"><button>搜索</button>
    </div>
    <div class="clearfix">
        <div class="left-menu">
            <ul type="none">
                <li><a href="/menu1/">菜單1</a></li>
                <li><a href="/menu2/">菜單2</a></li>
                <li><a href="/menu3/">菜單3</a></li>
            </ul>
        </div>
        <div class="content">
            home頁面             只須要改這裏,改爲其餘三個菜單對應的內容
        </div>
    </div>
    
    </body>
    </html>
  4. 用模板繼承來完成
    先創建一個母版 base.html文件,把相同的內容放進來
    <!DOCTYPE html>



    模板繼承

    </head>
    <body>
    
    <div class="nav">
        <a href="">外設首頁</a>
        <a href="">鼠標專賣</a>
        <a href="">鍵盤專賣</a>
        <a href="">耳麥專賣</a>
        <input type="text"><button>搜索</button>
    </div>
    <div class="clearfix">
        <div class="left-menu">
            <ul type="none">
                <li><a href="/menu1/">菜單1</a></li>
                <li><a href="/menu2/">菜單2</a></li>
                <li><a href="/menu3/">菜單3</a></li>
            </ul>
        </div>
        <div class="content">
            {% block content %}         這就是鉤子
                母版頁面
            {% endblock content %}
        </div>
    </div>
    
    </body>
    </html>

    首頁 home.html(其餘三個菜單的html文件同理):
    {% extends 'base.html' %} 繼承的語法

    {% block content %}           鉤子,寫本身的的內容,去替換母版鉤子的內容
        首頁頁面
    {% endblock content %}

    這樣一樣能完成以前的效果,而且節省的不少的代碼

注意的點:

  • 在模版中使用 {% extends %} 標籤,它必須是模版中的第一個標籤。其餘的任何狀況下,模版繼承都將沒法工做,模板渲染的時候django都不知道你在幹啥
  • 在base模版中設置越多的 {% block %} 標籤越好 (通常至少定義三個鉤子,html頁面一個,css一個,js一個),子模版沒必要定義所有父模版中的blocks,因此,你能夠在大多數blocks中填充合理的默認內容,而後,只定義你須要的那一個
  • 將子頁面的內容和繼承的母版中block裏面的內容同時保留
    {% block title %}
    {{ block.super }} #顯示母版的內容
    xxx # 顯示本身的內容
    {% endblock title %}
  • 爲了更好的可讀性,你也能夠給你的 {% endblock %} 標籤一個 名字
    {% block title %}
    xxx
    {% endblock title %}
  • 不能在一個模版中定義多個相同名字的 block 標籤。

6、組件

語法:{% include 'navbar.html' %}

能夠將經常使用的頁面內容如導航條,頁尾信息等組件保存在單獨的文件中,而後在須要使用的地方,文件的任意位置按以下語法導入便可。

例如:有個導航欄的 nav.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1{
            background-color: red;
            height: 40px;
        }
    </style>
</head>
<body>

<div class="c1">
    <div>
        <a href="">xx</a>
        <a href="">dd</a>
    </div>
</div>

</body>
</html>

此時有一個 test.html文件,直接嵌入以上導航欄的頁面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% include 'nav.html' %}      組件的語法
<h1>xxxxxxxxxx</h1>
</body>
</html>

這樣test.html文件沒有寫導航欄的代碼,卻依然拿到了導航欄

組件和插件的簡單區別

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

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

這二者的區別在 Javascript 裏區別很小,組件這個名詞用得很少,通常統稱插件。

7、自定義標籤和過濾器

自定義過濾器

  1. 在app應用文件中建立一個templatetags文件夾,文件夾必須是這個名字
  2. templatetags文件中建立一個xx.py文件,這個文件的名字能夠隨意起,好比叫 my_tags.py
  3. 在 my_tags.py 文件中建立自定義過濾器
    from django import template # 先導入 template

    register = template.Library()    # register至關於一個註冊器,名字是固定的,不可改變
    
    @register.filter       # 這樣使用註冊器,注意filter後不加括號
    def my_filter(v1):     # 不帶參數的過濾器
        s = v1 + '你真好' 
        return s
    
    @register.filter       # 這樣使用註冊器,注意filter後不加括號
    def my_filter(v1,v2):  # 帶參數的過濾器(最多傳遞一個參數)
        s = v1 + '你真好' + v2
        return s
  4. urls配置好路徑,views寫好視圖函數
    urls:
    url(r'^index/', views.index),

    views:
        def index(request):
         name = 'alex'
         return render(request,'test.html',{'name':name})
  5. 在模板的文件templates 中建立的 test.html 文件,就能夠經過引入使用了
    {% load my_tags %}
    {{ name|my_filter }} 頁面顯示:alex你真好

    {% load my_tags %}
    {{ name|my_filter:'呵呵' }}    頁面顯示:alex你真好呵呵
  6. 注意:自定義的過濾器最多隻能傳遞一個參數,多參數可考慮自定義標籤

自定義標籤

  1. 仍然是先在app建立templatetags文件夾,在文件中建立xx.py文件
  2. 建立自定義的標籤:
    @register.simple_tag
    def my_tag(v1,v2,v3): # 能夠傳遞多個參數
    s = v1 + '喜歡' + v2 + v3
    return s
  3. 在html文件中使用自定義標籤:
    {% my_tag name '吃燒烤' '哈啤酒' %} 頁面顯示:alex喜歡吃燒烤哈啤酒

inclusion_tag

多用於返回html代碼片斷 ,能夠將一個html文件先進行模板渲染,渲染完後,把html文件當成一個組件,放到你使用這個標籤的地方

  1. 依然是先在app建立templatetags文件夾,在文件中建立xx.py文件
  2. 建立自定義的inclusion_tag
    @register.inclusion_tag('test02.html')
    def func(v1):

    return {'data':v1}  此時v1接收的 l1 = [11,22,33]
    func的return數據,傳給了test02.html,做爲模板渲染的數據,將test02.htm渲染好以後,做爲一個組件,生成到調用這個func的地方
  3. 創建test02.html,封裝一些功能,至關於組件


      {% for d in data %}
    • {{ d }}

    • {% endfor %}

    此時,再去別的html文件中就能夠相似引用組件的把test02中的頁面引入進來,好比在test.html中:
    {% load my_tag %}
    {% func l1 %} l1是views函數傳遞過來的,此時又傳遞給func

8、靜態文件配置

js、css、img等都叫作靜態文件,那麼關於django中靜態文件的配置,咱們就須要在settings配置文件裏面配置一些內容

  1. 在項目的文件夾下創建一個文件夾,專門放靜態文件的,好比叫 jingtaiwenjian
  2. 在settings中配置:
    STATIC_URL = '/static/' # 指定路徑的別名

    STATICFILES_DIRS = [
        os.path.join(BASE_DIR,'jingtaiwenjian'),
    ]
  3. 調用靜態文件:
    在html文件中,想加入一個css的樣式,有如下方法引入靜態文件:

    方式一:
     <link rel="stylesheet" href="/jingtaiwenjian/mycss.css">  這樣寫是不對的,要用別名static
     <link rel="stylesheet" href="/static/mycss.css">
    
    方式二:
     {% load static %}
     <link rel="stylesheet" href="{% static 'mycss.css' %}">
    
    方式三:
     <link rel="stylesheet" href="{% get_static_prefix %}mycss.css">
    用別名 static 的優勢:
    • 別名也是一種安全機制,瀏覽器上經過調試臺你可以看到的是別名的名字,這樣別人就不能知道你靜態文件夾的名字了,提升了安全性,否則別人就能經過這個文件夾路徑進行攻擊。
    • 假如維護更新時,改變了靜態文件夾的名字,由於用了別名,因此也不會影響,提升了擴展性
    • 假如連別名也可能會修改,使用路徑的時候經過load static來找到別名,經過別名映射路徑的方式來獲取靜態文件

{% static %}

引入一張圖片:    
    {% load static %}
    <img src="{% static "images/hi.jpg" %}" alt="Hi!" />
    
引用JS文件時使用:
    {% load static %}
    <script src="{% static "mytest.js" %}"></script>
    
某個文件多處被用到能夠存爲一個變量
    {% load static %}
    {% static "images/hi.jpg" as myphoto %}
    <img src="{{ myphoto }}"></img>

{% get_static_prefix %} 獲取別名前綴的

{% load static %}
<img src="{% get_static_prefix %}images/hi.jpg" alt="Hi!" />

或者,用別名:
{% load static %}
{% get_static_prefix as STATIC_PREFIX %}

<img src="{{ STATIC_PREFIX }}images/hi.jpg" alt="Hi!" />
<img src="{{ STATIC_PREFIX }}images/hi2.jpg" alt="Hello!" />
相關文章
相關標籤/搜索