django-模板

4.模板

  • 做爲Web框架,Django提供了模板,能夠很便利的動態生成HTML
  • 模版系統致力於表達外觀,而不是程序邏輯
  • 模板的設計實現了業務邏輯(view)與顯示內容(template)的分離,一個視圖可使用任意一個模板,一個模板能夠供多個視圖使用
  • 模板包含
    • HTML的靜態部分
    • 動態插入內容部分
  • Django模板語言,簡寫DTL,定義在django.template包中
  • 由startproject命令生成的settings.py定義關於模板的值:javascript

    • DIRS定義了一個目錄列表,模板引擎按列表順序搜索這些目錄以查找模板源文件
    • APP_DIRS告訴模板引擎是否應該在每一個已安裝的應用中查找模板
  • 經常使用方式:在項目的根目錄下建立templates目錄,設置DIRS值
    DIRS=[os.path.join(BASE_DIR,"templates")]

     

模板處理

  • Django處理模板分爲兩個階段
  • Step1 加載:根據給定的標識找到模板而後預處理,一般會將它編譯好放在內存中
    loader.get_template(template_name),返回一個Template對象
  • Step2 渲染:使用Context數據對模板插值並返回生成的字符串
    Template對象的render(RequestContext)方法,使用context渲染模板
  • 加載渲染完整代碼:
    from django.template import loader, RequestContext
    from django.http import HttpResponse def index(request): tem = loader.get_template('ectest/index.html') context = RequestContext(request, {}) return HttpResponse(tem.render(context))

     

 

快捷函數

  • 爲了減小加載模板、渲染模板的重複代碼,django提供了快捷函數
  •  render_to_string("")
  • render(request,'模板',context)
    from django.shortcuts import render
    
    def index(request): return render(request, 'ectest/index.html')

     

4.1.定義模板

  • 模板語言包括
    • 變量
    • 標籤 { % 代碼塊 % }
    • 過濾器
    • 註釋{# 代碼或html #}

變量

  • 語法:
  • 當模版引擎遇到一個變量,將計算這個變量,而後將結果輸出
  • 變量名必須由字母、數字、下劃線(不能如下劃線開頭)和點組成
  • 當模版引擎遇到點("."),會按照下列順序查詢:
    • 字典查詢,例如:foo["bar"]
    • 屬性或方法查詢,例如:foo.bar
    • 數字索引查詢,例如:foo[bar]
  • 若是變量不存在, 模版系統將插入(空字符串)
  • 在模板中調用方法時不能傳遞參數
    {{ variable }}

 

在模板中調用對象的方法

  • 在models.py中定義類HeroInfo
    from django.db import models
    
    class HeroInfo(models.Model): ... def showName(self): return self.hname
  • 在views.py中傳遞HeroInfo對象
    from django.shortcuts import render
    from models import *
    
    def index(request): hero = HeroInfo(hname='abc') context = {'hero': hero} return render(request, 'ectest/detail.html', context)
  • 在模板detail.html中調用
    {{hero.showName}}

    注意:不能傳遞參數css

標籤

  • 語法:{ % tag % }
  • 做用
    • 在輸出中建立文本
    • 控制循環或邏輯
    • 加載外部信息到模板中供之後的變量使用
  • for標籤
    { %for ... in ...%}
      循環邏輯
    {{forloop.counter}}表示當前是第幾回循環
    { %empty%}   給出的列表爲空或列表不存在時,執行此處 { %endfor%}
  • if標籤
    { %if ...%}
      邏輯1
    { %elif ...%}   邏輯2 { %else%}   邏輯3 { %endif%}
  • include:加載模板並以標籤內的參數渲染
    { %include "foo/bar.html" % }
  • url:反向解析
    { % url 'name' p1 p2 %}
  • csrf_token:這個標籤用於跨站請求僞造保護
    { % csrf_token %}
  • 布爾標籤:and、or,and比or的優先級高
  • block、extends:詳見「模板繼承」
  • autoescape:詳見「HTML轉義」

 

過濾器

  • 語法:{ { 變量|過濾器 }},例如{ { name|lower }},表示將變量name的值變爲小寫輸出
  • 使用管道符號 (|)來應用過濾器
  • 經過使用過濾器來改變變量的計算結果
    # 能夠在if標籤中使用過濾器結合運算符
    if list1|length > 1
    
    # 過濾器可以被「串聯」,構成過濾器鏈
    name|lower|upper
    
    # 過濾器能夠傳遞參數,參數使用引號包起來
    list|join:", "
    
    # default:若是一個變量沒有被提供,或者值爲false或空,則使用默認值,不然使用變量的值
    value|default:"什麼也沒有"
    
    # date:根據給定格式對一個date變量格式化
    value|date:'Y-m-d'
  •  escape:詳見「HTML轉義」
  •  過濾器  https://blog.csdn.net/foryouslgme/article/details/52057445

註釋

  • 單行註釋
    {#...#}
  • 註釋能夠包含任何模版代碼,有效的或者無效的均可以
  • 使用comment標籤註釋模版中的多行內容
    # comment標籤
    { % comment % }
      多行註釋
    { % endcomment % }

示例

查詢全部英雄信息顯示出來,要求奇數行顯示爲紅色,偶數行顯示爲藍色html

  • 在views.py中定義圖書列表和圖書詳情頁的視圖函數
    views.py
  • 在booktest應用下的urls.py中添加相應url對象
    urls.py
  • 在模板目錄中添加index.html和detail.html
    index.html
    detail.html

 

4.2. 模板繼承

  •  模板繼承能夠減小頁面內容的重複定義,實現頁面內容的重用
  •  典型應用:網站的頭部、尾部是同樣的,這些內容能夠定義在父模板中,子模板不須要重複定義
  •  block標籤:在父模板中預留區域,在子模板中填充
  •  extends繼承:繼承,寫在模板文件的第一行
  •  定義父模板base.html
    { %block block_name%}
    這裏能夠定義默認值
    若是不定義默認值,則表示空字符串
    { %endblock%}
  •  定義子模板index.html
    { % extends "base.html" %}
  • 在子模板中使用block填充預留區域
    { %block block_name%}
    實際填充內容
    { %endblock%}

說明

  • 若是在模版中使用extends標籤,它必須是模版中的第一個標籤
  • 不能在一個模版中定義多個相同名字的block標籤
  • 子模版沒必要定義所有父模版中的blocks,若是子模版沒有定義block,則使用了父模版中的默認值
  • 若是發如今模板中大量的複製內容,那就應該把內容移動到父模板中
  • 使用能夠獲取父模板中block的內容
  • 爲了更好的可讀性,能夠給endblock標籤一個名字
    { % block block_name %}
    區域內容
    { % endblock block_name %}

三層繼承結構

  • 三層繼承結構使代碼獲得最大程度的複用,而且使得添加內容更加簡單
  • 以下圖爲常見的電商頁面

1.建立根級模板

  • 名稱爲「base.html」
  • 存放整個站點共用的內容
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}{% endblock %} 水果超市</title>
    </head>
    <body>
    <h1>top--{{logo}}</h1>
    <hr/>
    {% block content %}{% endblock %} <hr/> bottom </body> </html>

     

2.建立分支模版

  • 繼承自base.html
  • 名爲「base_*.html」
  • 定義特定分支共用的內容
  • 定義base_goods.html
    {% extends 'ectest/base.html' %}
    {% block title %}商品{% endblock title %} {% block content %} <table border="1"> <tr> <td height="300"> {% block left %}推薦商品{% endblock left %} </td> <td width="300" align="center"> {% block goods_content %} {% endblock %} </td> </tr> </table> {% endblock content%}
  • 定義base_user.html
    {% extends 'ectest/base.html'%}
    {% block title %}用戶中心{% endblock %} {% block content %} <table border="1"> <tr> <td height="300"> {% block left %} 用戶導航{% endblock left %} </td> <td width="300" align="center"> {% block user_content %} {% endblock %} </td> </tr> </table> {% endblock content%}
  • 定義index.html,繼承自base.html,不須要寫left塊
    {% extends 'ectest/base.html' %}
    {% block content %} 首頁內容 {% endblock content%}

3.爲具體頁面建立模板,繼承自分支模板java

  • 定義商品列表頁goodslist.html
    {%extends 'ectest/base_goods.html'%}
    {%block goods_content%} 商品正文列表 {%endblock goods_content%}
  • 定義用戶密碼頁userpwd.html
    {%extends 'ectest/base_user.html'%}
    {%block user_content%} 用戶密碼修改 {%endblock user_content%}

4.視圖調用具體頁面,並傳遞模板中須要的數據jquery

  • 首頁視圖index
    logo='welcome to igeek'
    def index(request):
        return render(request, 'ectest/index.html', {'logo': logo})
  • 商品列表視圖goodslist
    def goodslist(request):
        return render(request, 'ectest/goodslist.html', {'logo': logo})
  • 用戶密碼視圖userpwd
    def userpwd(request):
        return render(request, 'ectest/userpwd.html', {'logo': logo})

5.配置url

from django.conf.urls import url
from . import views urlpatterns = [ url(r'^$', views.index, name='index'), url(r'^list/$', views.goodslist, name='list'), url(r'^pwd/$', views.userpwd, name='pwd'), ]

 

4.3. HTML轉義

  • Django對字符串進行自動HTML轉義,如在模板中輸出以下值:
    視圖代碼:
    def index(request):
        return render(request, 'ectest/index2.html',
                      {
                          't1': '<h1>hello</h1>'
                      })
    模板代碼:
    {{t1}}
  • 顯示效果以下圖:

會被自動轉義的字符

  • html轉義,就是將包含的html標籤輸出,而不被解釋執行,緣由是當顯示用戶提交字符串時,可能包含一些攻擊性的代碼,如js腳本
  • Django會將以下字符自動轉義:
    < 會轉換爲&lt;
    
    > 會轉換爲&gt;
    
    ' (單引號) 會轉換爲&#39;
    
    " (雙引號)會轉換爲 &quot;
    
    & 會轉換爲 &amp;
  • 當顯示不被信任的變量時使用escape過濾器,通常省略,由於Django自動轉義
    {{t1|escape}}

關閉轉義

  • 對於變量使用safe過濾器
    {{ data|safe }}
  • 對於代碼塊使用autoescape標籤
    { % autoescape off %}
    {{ body }}
    { % endautoescape %}
  • 標籤autoescape接受on或者off參數
  • 自動轉義標籤在base模板中關閉,在child模板中也是關閉的

字符串字面值

  • 手動轉義
    { { data|default:"<b>123</b>" }}
    # 應寫爲:
    { { data|default:"&lt;b&gt;123&lt;/b&gt;" }}

 

4.4. csrf

  • 全稱Cross Site Request Forgery,跨站請求僞造
  • 某些惡意網站上包含連接、表單按鈕或者JavaScript,它們會利用登陸過的用戶在瀏覽器中的認證信息試圖在你的網站上完成某些操做,這就是跨站攻擊
  • 演示csrf以下
  • 建立視圖csrf1用於展現表單,csrf2用於接收post請求
    def csrf1(request):
        return render(request,'booktest/csrf1.html')
    def csrf2(request):
        uname=request.POST['uname']
        return render(request,'booktest/csrf2.html',{'uname':uname}) 
  • 配置url
    url(r'^csrf1/$', views.csrf1),
    url(r'^csrf2/$', views.csrf2),
  • 建立模板csrf1.html用於展現表單
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    <form method="post" action="/crsf2/">
        <input name="uname"><br>
        <input type="submit" value="提交"/>
    </form>
    </body>
    </html>
  • 建立模板csrf2用於展現接收的結果
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    {{ uname }}
    </body>
    </html>
  • 在瀏覽器中訪問,查看效果,報錯以下:
  • 將settings.py中的中間件代碼'django.middleware.csrf.CsrfViewMiddleware'註釋
  • 查看csrf1的源代碼,複製,在本身的網站內建一個html文件,粘貼源碼,訪問查看效果

防csrf的使用

  • 在django的模板中,提供了防止跨站攻擊的方法,使用步驟以下:
  • step1:在settings.py中啓用'django.middleware.csrf.CsrfViewMiddleware'中間件,此項在建立項目時,默認被啓用
  • step2:在csrf1.html中添加標籤
    <form>
    {% csrf_token %}
    ...
    </form>
  • step3:測試剛纔的兩個請求,發現跨站的請求被拒絕了,效果以下圖

取消保護

  • 若是某些視圖不須要保護,可使用裝飾器csrf_exempt,模板中也不須要寫標籤,修改csrf2的視圖以下
    from django.views.decorators.csrf import csrf_exempt
     
    @csrf_exempt
    def csrf2(request):
        uname=request.POST['uname']
        return render(request,'booktest/csrf2.html',{'uname':uname})
  • 運行上面的兩個請求,發現均可以請求

保護原理

  • 加入標籤後,能夠查看源代碼,發現多了以下代碼
    <input type='hidden' name='csrfmiddlewaretoken' value='nGjAB3Md9ZSb4NmG1sXDolPmh3bR2g59' />
  • 在瀏覽器的調試工具中,經過network標籤能夠查看cookie信息
  • 本站中自動添加了cookie信息,以下圖
  • 查看跨站的信息,並無cookie信息,若是加入上面的隱藏域代碼並添加cookie信息,發現又能夠訪問了
  • 結論:django的csrf不是徹底的安全
  • 當提交請求時,中間件'django.middleware.csrf.CsrfViewMiddleware'會對提交的cookie及隱藏域的內容進行驗證,若是失敗則返回403錯誤
 

4.5. 驗證碼

  • 在用戶註冊、登陸頁面,爲了防止暴力請求,能夠加入驗證碼功能,若是驗證碼錯誤,則不須要繼續處理,能夠減輕一些服務器的壓力
  • 使用驗證碼也是一種有效的防止crsf的方法
  • 驗證碼效果以下圖:

驗證碼視圖

  • 新建viewsUtil.py,定義函數verifycode
  • 此段代碼用到了PIL中的Image、ImageDraw、ImageFont模塊,須要先安裝Pillow(3.4.1)包,運行pip install pillow
  • 詳細文檔參考http://pillow.readthedocs.io/en/3.4.x/
  • Image表示畫布對象
  • ImageDraw表示畫筆對象
  • ImageFont表示字體對象,windows的字體路徑爲「C:\Windows\Fonts」
    # -*- coding: utf-8 -*-
    from django.http import HttpResponse, HttpResponseRedirect
    from PIL import Image, ImageFont, ImageDraw
    import random
    import cStringIO
    
    
    def verifycode(request):
        # 圖像的背景色
        bgcolor = (random.randrange(20, 100), random.randrange(20, 100), 255)
        # 圖像寬
        width = 100
        # 圖像高
        heigh = 25
    
        # 建立圖像對象
        img = Image.new('RGB', (width, heigh), bgcolor)
    
        # 建立能夠圖像對象繪圖的Draw對象
        draw = ImageDraw.Draw(img)
    
        # 在圖像上隨機100個座標上繪製畫點
        for i in range(0, 100):
            xy = (random.randrange(0, width), random.randrange(0, heigh))
            fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
            draw.point(xy, fill=fill)
    
        # 定義驗證碼的備選值
        str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
    
        # 在備選值中隨機選取4個值做爲驗證碼
        random_str = ''
        for i in range(4):
            random_str += str1[random.randrange(len(str1))]
    
        # 構造字體對象
        font = ImageFont.truetype('C:\Windows\Fonts\Arial.ttf', 23)
    
        # 構造字體顏色
        fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
    
        # 繪製4個值做爲驗證碼
        for t in range(4):
            draw.text((25 * t + 5, 2), random_str[t], font=font, fill=fontcolor)
    
        # 釋放Draw對象
        del draw
    
        # 將驗證碼存入session,用於作進一步驗證
        request.session['verifycode'] = random_str
    
        # 將圖片對象以類型爲png保存到內存中
        buf = cStringIO.StringIO()
        img.save(buf, 'png')
        
        # 將內存中的圖片數據返回給客戶端,MIME類型爲圖片png
        return HttpResponse(buf.getvalue(), 'image/png')

配置url

  • 在urls.py中定義請求驗證碼視圖的url
    from django.conf.urls import url
    from . import views, viewsUtil
    
    urlpatterns = [
        url(r'verifycode/$', viewsUtil.verifycode, name='verifycode'),
    ]

顯示驗證碼

  • 在viewsUtil.py,定義函數index
    from django.shortcuts import render
    
    def index(request):
        return render(request, 'booktest/index1.html')
  • 在urls.py中定義index視圖來顯示驗證碼圖片
    from django.conf.urls import url
    from . import views, viewsUtil
    
    urlpatterns = [
        url(r'^index/$', viewsUtil.index, name='index1'),
    ]
  • 在模板index1.html中使用img標籤,src指向驗證碼視圖
    <img id='verifycode' src="/verifycode/" alt="CheckCode">
  • 啓動服務器,查看顯示成功
  • 擴展:點擊「看不清,換一個」時,能夠換一個新的驗證碼
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <script type="text/javascript" src="/static/jquery-3.3.1.min.js"></script>
    <script type="text/javascript">
        $(function () {
            $('#verifycodeChange').css('cursor', 'pointer').click(function () {
                $('#verifycode').attr('src', $('#verifycode').attr('src')+1)
            });
        });
    </script>
    <img id='verifycode' src="/verifycode/?" alt="CheckCode">
    <span id="verifycodeChange">看不清,換一個</span>
  • 爲了可以實現提交功能,須要增長form和input標籤
    <form method="post" action="/verifycodeValid/">
        {% csrf_token %}
        <input type="text" name="vc">
        <img id='verifycode' src="/verifycode/?" alt="CheckCode">
        <span id="verifycodeChange">看不清,換一個</span>
        <br>
        <input type="submit" value="提交">
    </form>

     

驗證

  • 接收請求的信息,與session中的內容對比
    from django.http import HttpResponse, HttpResponseRedirect
    
    
    def verifycodeValid(request):
        vc = request.POST['vc']
        if vc.upper() == request.session['verifycode']:
            return HttpResponse('登陸成功')
        else:
            return HttpResponseRedirect(reverse('booktest:index1'))

     

  • 配置驗證處理的url
    from django.conf.urls import url
    from . import views, viewsUtil
    
    urlpatterns = [
        url(r'verifycodeValid/$', viewsUtil.verifycodeValid, name='verifycodeValid')
    ]

     

  • 能夠在網上搜索「驗證碼」,找到一些第三方驗證碼提供網站,閱讀文檔,使用到項目中
相關文章
相關標籤/搜索