Django-檢視&模版層
一、檢視函式
一個檢視函式,簡稱檢視,是一個簡單的Python 函式,它接受Web請求並且返回Web響應。
響應可以是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文件,或者一張圖片. . . 是任何東西都可以。
無論檢視本身包含什麼邏輯,都要返回響應。
程式碼寫在哪裡也無所謂,只要它在你的Python目錄下面。
除此之外沒有更多的要求了——可以說“沒有什麼神奇的地方”。
為了將程式碼放在某處,約定是將檢視放置在專案或應用程式目錄中的名為views.py的檔案中。
1、檢視請求物件
request物件:包含所有的請求資訊(瀏覽器給伺服器發過來的是一堆字串)
1. 屬性: 1.1 請求方式: request.method:獲取請求方式(GET/POST) # 位址列發過來的請求預設都是GET請求;<form mehtod="post">為POST請求<form action="" method="post"></form>中的action如果為空,預設是往當前頁面的url傳送請求;如果只寫了路徑(如 /login/),預設是往當前頁面的IP和埠拼接該路徑傳送請求 1.2 請求資料: request.GET:獲取所有的GET請求資訊(字典形式) request.POST:獲取所有的POST請求資訊(字典形式) 1.3 http://127.0.0.1:8000/index/?name=1&age=2 GET請求資料中某個值的獲取: request.GET.get("name") request.GET.get("age") request.POST.get("xxx") 同理1.4 request.path:獲取請求路徑(如:/login/) """ url組成: 協議(http):IP:port/路徑? get請求資料 ?為分隔符 如:http://127.0.0.1:8000/index/?name=1&age=2 """ # 127.0.0.1:8000 也有訪問路徑,根目錄、根路徑(/) # 在url控制器中新增根路徑: re_path(r"^$",views.index) # url控制器中 路徑和函式是一對一或多對一的關係 2. 方法: 2.1 request.get_full_path():獲取路徑和後面的get請求資料
2、檢視響應物件
響應物件主要有三種形式:
一、HttpResponse() :HttpResponse()括號內直接跟一個具體的字串作為響應體。(html標籤也能渲染出來)
二、render() :render(request,template_name[, context]) 如:render(request,"index.html",{"timer":ctime})
request: 用於生成響應的請求物件。
template_name:模板檔案;要使用的模板的完整名稱
context:可選引數;新增到模板上下文的一個字典。預設是一個空字典。如果第二個引數中的html檔案中有模板語法({{}})且字典中的某個值是可呼叫的,檢視將在渲染模板之前呼叫它。
render方法就是將一個模板頁面中的模板語法({{}})進行渲染,最終渲染成一個html頁面作為響應體。
三、redirect() :結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的 HttpResponse 物件。
1.傳遞要重定向的一個硬編碼的URL:
def my_view(request): ... return redirect('/some/url/')
2.也可以是一個完整的URL:
def my_view(request): ... return redirect('http://example.com/')
二、模版層
templates資料夾下面的檔案都叫模板檔案;
只不過有的包含模板語法(這種情況下,在把html頁面傳送給客戶端之前,會先有一個解析模板語法的過程)
1、模版語法之變數渲染
project(專案)的 urls.py
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path(r"index/",views.index) #新增新的記錄 ]
app01/views.py
from django.shortcuts import render # Create your views here. def index(request): """ 模板語法只有兩個: (1)、{{}} : 用於渲染變數 1. 深度查詢:句點符 2. 過慮器 (2)、 {% %} : 用於渲染標籤 :param request: :return: """ name = "neo" i = 10 l = [1,2,"a"] info = {"name":"neo","age":22} b = True class Person(object): def __init__(self,name,age): self.name = name self.age = age alex = Person("alex",33) egon = Person("egon",22) person_list = [alex,egon] # return render(request,"index.html",{"name":name}) # 如果變數特別多,可利用 locals():作用就是把所有的區域性變數按照字典的形式傳到 render()的第三個形參位置;傳的方式是按照 {"name":name,"i":i,...}這種方式 return render(request,"index.html",locals())
templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p>{{ name }}</p> <p>{{ i }}</p> <p>{{ l }}</p> <p>{{ b }}</p> <p>{{ alex }}</p> <p>{{ person_list }}</p> <p>{{ info }}</p> <hr> 深度查詢:利用點來完成 {# 想要列表l的第二個元素: l.1 #} <p>{{ l.1 }}</p> {# 獲取字典info中name那個key對應的value #} <p>{{ info.name }}</p> {# 獲取類物件alex的name屬性值 #} <p>{{ alex.name }}</p> {# 獲取person_list列表中第2個類物件的age屬性值 #} <p>{{ person_list.1.age }}</p> </body> </html>
2、模板之過慮器:
project(專案)urls.py
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path(r"index/",views.index), path(r"login/",views.login) #新記錄 ]
app01/views.py
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): # 深度查詢:句點符 name = "neo" i = 10 l = [1,2,"a"] info = {"name":"neo","age":22} b = True class Person(object): def __init__(self,name,age): self.name = name self.age = age alex = Person("alex",33) egon = Person("egon",22) person_list = [alex,egon] # 過慮器 import datetime now = datetime.datetime.now() empty_list = [] file_size = 1235467 str = "hello world" text = "Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of Web development, so you can focus on writing your app without needing to reinvent the wheel. It’s free and open source." link = "<a href='#'>click</a>" # 模板之標籤 num = 90 # return render(request,"index.html",{"name":name}) # 如果變數特別多,可利用 locals():作用就是把所有的區域性變數按照字典的形式傳到 render()的第三個形參位置;傳的方式是按照 {"name":name,"i":i,...}這種方式 return render(request,"index.html",locals()) def login(request): if request.method == "POST": return HttpResponse("OK") return render(request,"login.html") # login.html中含有模板語法,因為有{% csrf_token %};所以傳送給客戶端之前會先渲染模板語法
templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p>{{ name }}</p> <p>{{ i }}</p> <p>{{ l }}</p> <p>{{ b }}</p> <p>{{ alex }}</p> <p>{{ person_list }}</p> <p>{{ info }}</p> <hr> {#深度查詢:利用點來完成#} {# 想要列表l的第二個元素: l.1 #} <p>{{ l.1 }}</p> {# 獲取字典info中name那個key對應的value #} <p>{{ info.name }}</p> {# 獲取物件alex的name屬性值 #} <p>{{ alex.name }}</p> {# 獲取person_list列表中第2個物件的age屬性值 #} <p>{{ person_list.1.age }}</p> <hr> <h3>過慮器</h3> {# 時間型別會自動給你渲染好格式,如:June 6, 2018, 4:32 p.m. #} {{ now }} {# 把上述時間型別通過過慮器轉化格式;date是Django提供的過慮器名字,固定的 #} {{ now|date:"Y-m-d" }} {#過慮器#} {# default :如果一個變數是false或者為空,使用給定的預設值。否則,使用變數的值。#} {{ empty_list|default:"資料為空" }} {#length:返回值的長度。它對字串和列表都起作用。#} {{ person_list|length }} {#filesizeformat:將值格式化為一個 “人類可讀的” 檔案尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)。#} <p>{{ file_size|filesizeformat }}</p> {#date:如果 value=datetime.datetime.now(),同上#} {#slice:把字串按照切片的功能進行切割顯示(顧頭不顧尾)#} {{ str|slice:"2:-1" }} {#truncatechars:如果字串字元多於指定的字元數量,那麼會被截斷。截斷的字串將以可翻譯的省略號序列(“...”)結尾(...也佔3個字元)。 引數:要截斷的字元數#} <p>{{ text|truncatechars:20 }}</p> {#truncatewords:按照單詞擷取#} <p>{{ text|truncatewords:6 }}</p> {#safe:Django的模板中會對HTML標籤和JS等語法標籤進行自動轉義;為了在Django中關閉HTML的自動轉義有兩種方式,如果是一個單獨的變數我們可以通過過濾器“|safe”的方式告訴Django這段程式碼是安全的不必轉義。#} <p>{{ link }}</p> {#瀏覽器效果:<a href='#'>click</a>#} <p>{{ link|safe }}</p> {#add:加法;如下:讓其加100#} <p>{{ l.0|add:100 }}</p> {#upper:轉成大寫#} <p>{{ name|upper }}</p> <hr> {#標籤#} {#for標籤:遍歷每一個元素#} {#i就是l列表中的元素#} {% for i in l %} <p>{{ i }}</p> {% endfor %} {#可以利用{% for obj in list reversed %}反向完成迴圈。#} {#i是info字典中的每個key#} {% for i in info %} <p>{{ i }}</p> {% endfor %} {#遍歷一個字典#} {% for key,val in info.items %} <p>{{ key }}:{{ val }}</p> {% endfor %} {% for person in person_list %} <p>{{ person.name }} {{ person.age }}</p> {% endfor %} {#迴圈序號可以通過{{forloop}}顯示;{{forloop}}必須要寫在迴圈內部;forloop.counter是從1開始,forloop.counter0是從0開始 #} {% for i in l %} <p>{{ forloop.counter}} {{ i }}</p> {% endfor %} {#for...empty:for 標籤帶有一個可選的{% empty %} 從句,以便在給出的組是空的或者沒有被找到時,可以有所操作。#} {% for i in empty_list %} <p>{{ i }}</p> {% empty %} <p>內容為空</p> {% endfor %} {#如果empty_list為空,就會顯示{% empty %}後面的 <p>內容為空</p> 標籤#} <hr> {#if標籤:{% if %}會對一個變數求值,如果它的值是“True”(存在、不為空、且不是boolean型別的false值),對應的內容塊會輸出。#} {% if num > 100 or num < 0 %} <p>無效</p> {% elif num > 80 and num < 100 %} <p>優秀</p> {% else %} <p>湊合吧</p> {% endif %} {#with:使用一個簡單地名字快取一個複雜的變數,當你需要使用一個“昂貴的”方法(比如訪問資料庫)很多次的時候是非常有用的#} {% with person_list.1.age as p_age %} {# 此時 p_age 就代表 person_list.1.age #} {{ p_age }} {{ p_age }} {% endwith %} {#csrf_token:這個標籤用於跨站請求偽造保護#} </body> </html>
templates/login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> {# csrf_token:這個標籤用於跨站請求偽造保護;(可以給當前頁面傳送POST請求) #} {% csrf_token %} <input type="text" name="user"> <input type="submit"> </form> </body> </html>
模版語法之自定義標籤和過濾器
步驟:
- 在settings中的INSTALLED_APPS配置當前app,不然django無法找到自定義的標籤或者過濾器
- 在app中建立templatetags模組(模組名只能是templatetags)
- 建立任意 .py 檔案,如:my_tags.py;在其中定義自己的過濾器和標籤
- 在使用自定義過濾器的html檔案中匯入之前建立的 my_tags.py
- 使用自定義的過濾器或標籤
目錄結構
app01/views.py
from django.shortcuts import render # Create your views here. def index(request): i = 10 return render(request,"index.html",locals()) #locals 所有變數到html裡面
app01/templatetags/my_tags_filters.py
from django import template register = template.Library() # register的名字是固定的,不可改變 @register.filter def multi_filter(x,y): return x*y # 一個函式加上裝飾器 @register.filter 就變成了一個自定義的過濾器;上述過濾器實現的功能是兩個數相乘 # 自定義的過濾器要在模板語法中使用 # 自定義過濾器的侷限性:第一個形參是被修飾的變數,第二個形參是過濾器中的引數;過濾器最多隻能放兩個引數 # 自定義過濾器的優勢:進行邏輯判斷時,只能用過濾器 # 自定義標籤 @register.simple_tag def multi_tag(x,y): return x*y # 自定義標籤無引數個數的限制
templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>自定義過濾器,標籤</h3> {#在使用自定義的過濾器的html檔案中匯入之前建立的 my_tags_filters.py#} {% load my_tags_filters %} <p>{{ i|multi_filter:20 }}</p> {#函式multi_filter中有兩個形參,所有過濾器multi_filter中要給一個引數,如上面的20#} {#呼叫自定義標籤#} <p>{% multi_tag 7 9 %}</p> {#自定義過濾器的優勢#} {#變數乘以20如果大於100就顯示100,否則就顯示它本身#} {% if i|multi_filter:20 > 100 %} <p>100</p> {% else %} <p>{{ i }}</p> {% endif %} {#自定義標籤不能放到流程控制判斷中#} </body> </html>
模板繼承(extends)
模版繼承可以讓你建立一個基本的“骨架”模版,它包含您站點中的全部元素,並且可以定義能夠被子模版覆蓋的blocks。
目錄結構
urls.py
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path(r'index/',views.index), path(r"orders/",views.orders) ]
views.py
from django.shortcuts import render # Create your views here. def index(request): i = 10 return render(request,"index.html",locals()) def orders(request): return render(request,"orders.html")
base.html (需要引入的模板樣式;extends語法)
<!DOCTYPE html> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {#再放一個盒子用於重新寫title標籤;可留多個盒子用於以後擴寫內容#} {% block title %} <title>base</title> {% endblock %} {# block區域裡面本身也可以寫內容,如上面的block寫入 <title>base</base> ,這樣如果引用這個模板時不重寫 title這個block,就會預設會使用 block中原有的內容 #} <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <style> * { padding: 0; margin: 0; } .header { width: 100%; height: 50px; background-color: blue; } </style> </head> <body> {#模板語法之繼承#} <div class="header"></div> <div class="container"> <div class="row"> <div class="col-md-3"> {# 想在這個地方呼叫advertise.html,就用include語法#} {% include "advertise.html" %} </div> <div class="col-md-9"> {# 繼承:col-md-9 中的內容都刪除#} {# 在這個地方寫一個 block,意味著在這留了一個區域等待別人來重新寫 #} {% block con %} {% endblock %} </div> </div> </div> </body> </html>
orders.html(在其中引入 base.html這個模板)
{#extends需要放在第一行#} {#extends標籤:用於繼承;把 base.html 的樣式全都拿過來(頭部和左側欄樣式),然後 col-md-9中的內容自己再重新寫#} {% extends "base.html" %} {#引入過來 base.html的樣式之後,需要在 block中填充內容#} {% block con %} {# 重新寫自己的內容 #} <h4>訂單</h4> {% endblock %} {#重新寫title;放一個HTML的title標籤#} {% block title %} <title>orders</title> {% endblock %} {#過程:先通過 extends 來繼承樣式,再通過 block 來填充內容#} {#繼承用於解決HTML程式碼的複用性#}
index.html(include語法)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <style> * { padding: 0; margin: 0; } .header { width: 100%; height: 50px; background-color: blue; } </style> </head> <body> {#模板語法之繼承#} <div class="header"></div> <div class="container"> <div class="row"> <div class="col-md-3"> {# 想在這個地方呼叫advertise.html,就用include語法#} {% include "advertise.html" %} </div> <div class="col-md-9"> <h3>自定義過濾器,標籤</h3> {#在使用自定義的過濾器的html檔案中匯入之前建立的 my_tags_filters.py#} {% load my_tags_filters %} <p>{{ i|multi_filter:20 }}</p> {#函式multi_filter中有兩個形參,所有過濾器multi_filter中要給一個引數,如上面的20#} {#呼叫自定義標籤#} <p>{% multi_tag 7 9 %}</p> {#自定義過濾器的優勢#} {#變數乘以20如果大於100就顯示100,否則就顯示它本身#} {% if i|multi_filter:20 > 100 %} <p>100</p> {% else %} <p>{{ i }}</p> {% endif %} {#自定義標籤不能放到流程控制判斷中#} </div> </div> </div> </body> </html>
advertise.html(被 index.html引入的內容)
<div class="action"> <div class="panel panel-danger"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> 111 </div> </div> <div class="panel panel-warning"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> 222 </div> </div> <div class="panel panel-success"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> 333 </div> </div> </div>
注意:
如果你在模版中使用 {% extends %} 標籤,它必須是模版中的第一個標籤。其他的任何情況下,模版繼承都將無法工作。
在base模版中設定越多的 {% block %} 標籤越好。子模版不必定義全部父模版中的blocks,所以,你可以在大多數blocks中填充合理的預設內容,然後,只定義你需要的那一個。多一點鉤子總比少一點好。
如果你發現你自己在大量的模版中複製內容,那可能意味著你應該把內容移動到父模版中的一個 {% block %} 中。
為了更好的可讀性,你也可以給你的 {% endblock %} 標籤一個 名字 。例如:
{% block content %} #開始 content 是名字 ... {% endblock content %} #結束 content 是名字
在大型模版中,這個方法幫你清楚的看到哪一個 {% block %}
標籤被關閉了
不能在一個模版中定義多個相同名字的block標籤