1. 程式人生 > 實用技巧 >django修身大法之美麗誘惑

django修身大法之美麗誘惑

模板

模板⽤於快速⽣成動態⻚⾯返回給客戶端,模板是⼀個⽂本,⽤於分離⽂檔的表現
形式和內容。 模板定義了佔位符以及各種⽤於規範⽂檔該如何顯示的模板標籤。
模板通常⽤於產⽣HTML,但是Django的模板也能產⽣任何基於⽂本格式的⽂檔。
模板包含兩部分:

  • html程式碼
  • 模板標籤

⼀、模板位置

  • 在應⽤中建⽴templates⽬錄,好處不需要註冊,不好的地⽅,有多個應⽤的
    時候不能復⽤⻚⾯

  • 第⼆種是放在⼯程的⽬錄下,好處是如果有多個應⽤,可以調⽤相同的⻚⾯,
    需要註冊

    • 需要修改項⽬的配置⽂件settings.py
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates', # 這裡預設使用Django自帶的模板引擎
            'DIRS': [os.path.join(BASE_DIR, 'templates')] # 模板絕對路徑,如果是自己手動建立的django專案,則該內容為空,需要手動設定
            ,
            'APP_DIRS': True, # 是否在應用目錄下查詢模板檔案
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    

    Django 模板查詢機制: Django首先會查詢DIRS下配置的模板路徑,如果不在的話,則是在每個 app 的 templates
    ⽂件夾中找(⽽不只是當前 app 中的程式碼只在當前的 app 的 templates ⽂件
    夾中找)。各個 app 的 templates 形成⼀個⽂件夾列表,Django 遍歷這個列
    表,⼀個個⽂件夾進⾏查詢,當在某⼀個⽂件夾找到的時候就停⽌,所有的都
    遍歷完了還找不到指定的模板的時候就是 Template Not Found (過程類似於
    Python找包)。這樣設計有利當然也有弊,有利是的地⽅是⼀個app可以⽤另
    ⼀個app的模板⽂件,弊是有可能會找錯了。所以我們使⽤的時候在
    templates 中建⽴⼀個 app 同名的⽂件夾,這樣就好了。

⼆、模板的渲染

2.1 loader載入

好處是可以載入⼀次模板,然後進⾏多次渲染

from django.template import loader #導⼊loader

def index(request):
     temp = loader.get_template('index.html')
     # 渲染模板,⽣出html原始碼
     res = temp.render(context={'content':'hello index'})
     print(res)
     return HttpResponse(res)

2.2 render

from django.shortcuts import render

render(request,templatesname,context=None)
引數: 
    request:請求物件
    templatesname:模板名稱
    context:引數字典,必須是字典

三、模板語法

django模板中包括兩部分:變數和內建標籤。變數會在模板渲染時被其值代替,
內建標籤負責邏輯控制。

3.1 變數

變數在模板中的表示為:{{ 變數名 }},變數名就是render中context中的鍵。變數
可以基本型別中的數值、字串、布林,也可以是字典、物件、列表等。django
提供了點號來訪問複雜資料結構。

  • 列表、元組的元素可以使⽤索引引⽤,不能使⽤負索引,語法:變數.索引
  • 字典: 字典變數.key
  • 物件: 物件.屬性 物件.⽅法名(⽅法不能有引數)

當模板系統在變數名中遇到點時,按照以下順序嘗試進⾏查詢:

  • 字典型別查詢
  • 屬性查詢
  • 方法呼叫
  • 列表型別索引

如果模板中引⽤變數未傳值,則會被置為空,不會報錯,除⾮你對其進⾏了操作。

3.2 過濾器

過濾器是在變數顯示之前修改它的值的⼀個⽅法,過濾器使⽤管道符。過濾器可以
串聯呼叫

語法格式:

{{ 變數|方法 }}

常⻅的過濾器⽅法:

方法名 作用 示例
default 預設值 {{ li|default:"預設值"}}
default_if_none 如果變數是none,則顯示預設值 {{ value|default_if_none:'"hello" }}
cut 從字元中刪除指定字元 {{ value|cut:" "}} 刪除value中所有的空格
length 獲取字串或列表的⻓度 {{ str1|length}}
lower 將所有字⺟都變為⼩寫 {{ value|lower}}
upper 將所有字⺟都變為⼤寫 {{ value|upper}}
truncatechars 擷取字串前n個字元 {{ value|truncatechars:9 }}
date 格式化⽇期字串 {{ value|date:"Y-m-d H:i:s"}}
add 增加變數的值 {{ num|add:"3"}}
divisibleby 把變數的值除以指定值 {{ value|divisibleby:"3"}}
first 獲取列表第⼀個元素 {{ value|first }}
last 獲取列表最後⼀個元素 {{ value|last }}
join 將列表內容連結為一個字串 {{ value|join:'-' }}
autoescape 設定或取消轉義 {% autoescape off %}{{ data }}{%endautoescape %}
  • ⾃定義過濾器

    內建過濾器功能有限,如果不能滿足需求,可以自己定義過濾器。

    • 在app⾥建立⼀個包:templatetags(名字必須是這個,且為包)

    • 在包⾥建立⼀個py⽂件

      from django import template
      # 例項化⾃定義過濾器註冊物件
      register = template.Library()
      # name代表在模板中使⽤的過濾器的名稱
      @register.filter(name='hello')
      def hello(value,arg):
           """
           :param value: 傳給hello過濾的值
           :param arg: hello⾃帶的引數
           :return:
           """
           return value + str(arg)
       
      @register.filter('time_ago')
          def time_ago(value):
           """
      
          定義⼀個距離當前時間多久之前的過濾器
           :param value:
           :return:
           1.如果時間間隔⼩於1分鐘內,那麼就顯示剛剛
           2.如果時間間隔⼤於1分鐘⼩於1⼩時,那麼就顯示xx分鐘前
           3.如果時間間隔⼤於1⼩時⼩於24⼩時,那麼就顯示xx⼩時前
           4.如果時間間隔⼤於24⼩時⼩於30天,那麼就顯示xx天前
           5.如果時間間隔⼤於30天,那麼就顯示具體時間
           """
           if not isinstance(value, datetime.datetime):
               return value
           now = datetime.datetime.now()
           timestamp = (now - value).total_seconds()
           if timestamp < 60:
               return '剛剛'
           elif timestamp >= 60 and timestamp < 60 * 60:
               return '{}分鐘前'.format(int(timestamp / 60))
           elif timestamp >= 60 * 60 and timestamp < 60 * 60 * 24:
               return '{}⼩時前'.format(timestamp / 60 / 60)
           elif timestamp >= 60 * 60 * 24 and timestamp < 60 * 60 * 23 *
          30:
               return‘{}天前'.format(int(timestamp/68/68/24))
      	else:
       		return value.strftime('%Y-%m-%d %H:%M')
      
      
    • 在模板中使⽤

      {% load customfilter %} #載入⾃定義過濾器的模組
      <!DOCTYPE html>
      <html lang="en">
      <head>
       <meta charset="UTF-8">
       <title>Title</title>
      </head>
      <body>
      {{ name |hello:' how are you' }} #使⽤⾃定義過濾器
      </body>
      </html>
      

3.3 內建標籤

語法:{% tag %}

1.if標籤

{% if express1 %}
	# to do
{% elif express2 %}
	# to do
{% else %}
	# to do
{% endif %}
  • if表示式中使⽤以下運算子(優先順序從⾼到低):
    • < >= <= == !=
    • in 、not in
    • is、is not
    • not
    • and
    • or
  • 不要在表示式中使⽤(),可以使⽤if巢狀實現功能
  • 不⽀持 if 3 < b < 5這種寫法

2.for

遍歷可迭代物件

{% for x in y %}
 
...
{% endfor %}
  • 反向迭代(reversed)
{% for value in c [1,2,3,4,5] reversed %}
 <span>{{ value }}---</span>
{% endfor %}
  • empty 當可迭代物件為空或不存在時執⾏,否則不執⾏
{% for value in c %}
	<span>{{ value }}---</span>
{% empty %}
	資料不存在
{% endfor %}
  • 字典迭代
# e = {'a1':20,'b1':40}
{% for k,v in e.items %}
<div>{{ k }}---{{ v }}</div>
{% endfor %}
  • 獲取for迴圈迭代的狀態
變數名稱 變數說明
forloop.counter 獲取迭代的索引 從1開始
forloop.counter0 獲取迭代的索引 從0開始
forloop.revcounter 迭代的索引從最⼤遞減到1
forloop.revcounter0 迭代的索引從最⼤遞減到0
forloop.first 是否為第⼀次迭代
forloop.last 是否為最後⼀次迭代
forloop.parentloop 獲取上層的迭代物件
{% for i in c %}
 <li>{{ forloop.first }}</li>
 <li>{{ forloop.last }}</li>
 <li>{{ forloop.counter }}</li>
 <li>{{ forloop.counter0 }}</li>
 <li>{{ forloop.revcounter }}</li>
 <li>{{ forloop.revcounter0 }}</li>
{% endfor %}

3. ifequal/ifnotequal

⽤於判斷兩個值相等或不等的

{% ifequal var var %}
{% endifequal %}
{% ifnotequal var var %}
{% endifnotequal %}

4.註釋

  • 單行註釋
{# 註釋內容 #}
  • 多行註釋
{% comment %}
...
{% endcomment %}

5.跨站請求偽造 csrf

防⽌⽹站受第三⽅伺服器的惡意攻擊(確定表單到底是不是本⽹站的表單傳遞過來
的)。csrf相當於在表達中增加了⼀個隱藏的input框,⽤於向伺服器提交⼀個唯⼀
的隨機字串⽤於伺服器驗證表單是否是本伺服器的表單。

使⽤:

settings.py

MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]

表單裡

<form action=
"" method="post">
 {% csrf_token %}
 
<input type="text" name="username">
 
<p><input type="submit"></p>
</form>
  • 全站禁用csrf
#在settings中設定
MIDDLEWARE = [
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 #'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
  • 區域性禁⽤csrf
#在不想檢驗csrf的檢視函式前新增裝飾器@csrf_exempt。
from django.views.decorators.csrf import csrf_exempt,csrf_protect

@csrf_exempt
def csrf1(request):
 pass
  • ajax驗證csrf
Ajax提交資料時候,攜帶CSRF:
a. 放置在data中攜帶
<form method="POST" action="/csrf1.html">
 {% csrf_token %}
 <input id="username" type="text" name="username" />
 <input type="submit" value="提交"/>
 <a onclick="submitForm();">Ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
 function submitForm(){
 var csrf = $('input[name="csrfmiddlewaretoken"]').val();
 var user = $('#user').val();
 $.ajax({
 url: '/csrf1.html',
 type: 'POST',
 data: { "user":user,'csrfmiddlewaretoken': csrf},
 success:function(arg){
 console.log(arg);
 }
 })
 }
</script>千

注意:

csrf的意義在於 給每⼀個表單都設定⼀個唯⼀的csrf的值 並且cookie也儲存⼀份
當提交表單過來的時候 判斷cookie中的值 和csrf_token中的值 是否都為本⽹站⽣
成的 如果驗證通過則提交 否則 403

6.模板導⼊標籤( include)

可以把指定html⽂件程式碼導⼊到當前⽂件,實現模板程式碼的復⽤/重⽤。語法格
式:

{% include '路徑/xxx.html' %}

7.url標籤

在模板中url標籤可⽤於反向解析

<h2><a href="{% url 'App:index' %}">動態⽣成路由地址不帶參的跳轉</a>
</h2>
<h2><a href="{% url 'App:args1' 1 2 %}">動態⽣成路由地址帶參的跳轉</a>
</h2>
<h2><a href="{% url 'App:args1' num1=1 num2=2 %}">動態⽣成路由地址帶關
鍵字引數的跳轉</a></h2>

四、模板繼承

在整個⽹站中,如何減少共⽤⻚⾯區域(⽐如站點導航)所引起的重複和冗餘代
碼?Django 解決此類問題的⾸選⽅法是使⽤⼀種優雅的策略—— 模板繼承 。
本質上來說,模板繼承就是先構造⼀個基礎框架模板,⽽後在其⼦模板中對它所包
含站點公⽤部分和定義塊進 ⾏過載。

  • {% extends %} 繼承⽗模板
  • {% block %} ⼦模板可以過載這部分內容。
  • {{ block.super }}調⽤⽗模板的程式碼

使⽤繼承的⼀種常⻅⽅式是下⾯的三層法:

  • 建立base.html模板,在其中定義站點的主要外觀感受。這些都是不常修改甚
    ⾄從不修改的部分。
  • 為每種型別的⻚⾯建立獨⽴的模板,例如論壇⻚⾯或者圖⽚庫。這些模板拓展
    相應的區域模板。
  • ⾃⼰的⻚⾯繼承⾃模板,覆蓋⽗模板中指定block

注意事項:

  • 如果在模板中使⽤ {% extends %} ,必須保證其為模板中的第⼀個模板標記。
    否則,模板繼承將不起作 ⽤。
  • ⼀般來說,基礎模板中的 {% block %} 標籤越多越好。
  • 如果發覺⾃⼰在多個模板之間有重複程式碼,你應該考慮將該程式碼放置到⽗模板
    的某個 {% block %} 中。
  • 不在同⼀個模板中定義多個同名的 {% block %} 。
  • 多數情況下, {% extends %} 的引數應該是字元,但是如果直到運⾏時⽅能確
    定⽗模板名稱,這個引數也 可以是個變數。

五、靜態資源配置

什麼是靜態資源:css、js、images 需要從外部導⼊的資源

5.1建立static⽂件夾(通常放在根⽬錄下)

5.2需要在settings註冊

STATIC_URL='/static/'

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

5.3在模板中使⽤靜態資源

{% load static %} #放置到模板開頭
 <img src="/static/img/img.jpeg" alt=""> #硬編碼
 <img src="{% static 'img/img.jpeg' %}" alt=""> #動態寫法,建議⽤這種

六、jinja2模板引擎配置

  • 安裝jinja2模板引擎
pip install jinja2
  • 設定jinja2環境變數
# -*- coding: utf-8 -*-
from jinja2 import Environment
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse


def jinja2_environment(**options):
    env = Environment(**options)
    env.globals.update({
        'static': staticfiles_storage.url,
        'url': reverse,
    })
    return env
  • 配置(setting.py)

    • 獨⽴使⽤jinja2,不使⽤Django模板引擎
    #獨⽴使⽤jinja2
    INSTALLED_APPS = [
     #'django.contrib.admin', # 註釋了admin
     .....
    ]
    #模板配置
    TEMPLATES = [{
     'BACKEND':
    'django.template.backends.jinja2.Jinja2',#jinja2模版
     'DIRS': [
     os.path.join(BASE_DIR, 'templates'),#模版⽂件位置
     ],
     'APP_DIRS': True, 
     'OPTIONS': {
     'context_processors': [
     'django.template.context_processors.debug',
     'django.template.context_processors.request',
     
     
    'django.contrib.auth.context_processors.auth',
     
     
    'django.contrib.messages.context_processors.messages',
     ],
     'environment': 'App.jinja2_env.environment', # 配置環
    境,jinja的配置⽂件位置
     },
     },
     ] 
    
    • 兩個同時使⽤
    #模板配置
    TEMPLATES = [{
     'BACKEND': 'django.template.backends.jinja2.Jinja2',#jinja2模
    版
     'DIRS': [
     os.path.join(BASE_DIR, 'templates2'),#修改模版⽂件位置
     ],
     'APP_DIRS': True, 
     'OPTIONS': {
     'context_processors': [
     'django.template.context_processors.debug',
     'django.template.context_processors.request',
     'django.contrib.auth.context_processors.auth',
     
     
    'django.contrib.messages.context_processors.messages',
     ],
     'environment': 'App.jinja2_env.environment', # 配置環
    境,jinja的配置⽂件位置
     },
     },
     {
     'BACKEND': 'django.template.backends.django.DjangoTemplates',
     'DIRS': [os.path.join(BASE_DIR, 'templates')]
     ,
     'APP_DIRS': True,
     'OPTIONS': {
     'context_processors': [
     'django.template.context_processors.debug',
     'django.template.context_processors.request',
     'django.contrib.auth.context_processors.auth',
     
     
    'django.contrib.messages.context_processors.messages',
     ],
     },
     },
     ]
    
    
  • 靜態資源和url

<p>
 <a href="{{ url('app:index') }}">dddd</a>
</p>
<img src="{{ static('images/1.jpg') }}" alt="">