Python Django 學習紀錄(三) 視圖與模板
一、Django的Framework架構:
Django是採用MTV架構,它和MVC架構類似,都是近年來大型專案開發的主流方式。
1.認識MVC:
採用MVC架構設計軟體系統會比較好維護、擴充、除錯、且分工明確
MVC是軟體設計的一 種架構,將軟體系統分為三個部分:
模型(Model):代表商業邏輯與資料庫存取有關的程式
視圖(View):代表輸入和輸出畫面的呈現
控制(Controller):介於Model和View之間,判斷該呼叫哪個Model存取資料庫必透過哪個View介面輸入表單或顯示資料
2.Django的MTV架構:
Django採用的MTV架構分為Model、Template、View
Model:代表商業邏輯與資料庫存取有關的程式
Template(.html):當作介面輸入表單或顯示資料,取代MVC的View
View:處理Model存取資料庫和Template介面資料的輸入或顯示,取代MVC
※MTV以Template模板(.html檔)輸入或輸出資料,在<views.py>檔定義函式,利用該函式透過Model存取資料庫,同時將資料以Template模板顯示
※在<views.py>函式取得的資料,必須傳給Template模板,最簡單的方式就是透過render函式,然後在Template模板顯示傳遞的資料
二、視圖和模板:
實例說明視圖(View)和模板(Template)
1.建立試圖和模板:
(1)定義url
在學習紀錄(二)已經使用過模板。透過<urls.py>檔定義url,瀏覽hello4/xxx/執行hello4自訂函數:
from django.contrib import admin
from django.urls import path
from django.conf.urls import url #導入url套件
from myapp.views import hello4 #導入hello4自訂函數
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^hello4/(\w+)/$',hello4),
]
(2)定義View視圖
在<views.py>檔定自hello4自訂函數,以render函式呼叫模板,以locals()將所有區域變數傳遞給<hello4.html>模板:
from django.shortcuts import render def hello4(request,username): now = datetime.now() #取得現在日期時間 return render(request,"hello4.html",locals()) #render(request傳遞GET或POST,模板名稱,傳遞所有區域變數)
(3)建立Template模板
<hello4.html>檔視MTV中的Template模板,建立在Template目錄中,內容如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>顯示圖片的模板</title> {% load staticfiles %} <link href = "{% static "css/style.css" %}" rel="stylesheet" type="text/css"/> </head> <body> <div id="home"> <img src="{% static "images/potato.jpg" %}" alt="歡迎光臨" width="32" height="32" /> <span class = "info">歡迎光臨: {{username}}</span> <h2>{{now}}</h2> </div> </body> </html>
MTV的model需要配合資料庫,之後會再作重點整理
2.傳遞變數到Template模板檔案:
在View視圖可以使用render函式傳遞字典給顯示的模板
傳遞字典到顯示模板:
使用render函式,必須先import該套件
第一個參數是HttpRequest物件
第二個參數是模板名稱
第三個參數是字典
from django.shortcuts import render #render(request, template_name, context)
例如傳遞字典「{"potato":1}」給顯示模板<ivan.html>
render(request, "ivan.html", {"potato":1})
傳遞多筆字典「{"name":"ivan","age":22}」給顯示模板<ivan.html>
render(request, "ivan.html", {"name":"ivan","age":22})
3.範例實作:
建立一個名為passdata的專案,接著建立名為passdataapp的app,templates目錄、static目錄、migration資料檔,利用migrate將模型與資料庫同步。
記得接下來要完成<setting.py>的設置:
""" Django settings for passdata project. Generated by 'django-admin startproject' using Django 2.0.1. For more information on this file, see https://docs.djangoproject.com/en/2.0/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/2.0/ref/settings/ """ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'li)-4-ce72bvaqxb_+o=%0b#8g3!^7=q73)k1o8li166mb44l1' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'passdata' ] 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', ] ROOT_URLCONF = 'passdata.urls' TEMPLATES = [ { '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', ], }, }, ] WSGI_APPLICATION = 'passdata.wsgi.application' # Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ LANGUAGE_CODE = 'zh-Hant' TIME_ZONE = 'Asia/Taipei' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
再來完成<urls.py>的定義:
會有「/dice/」執行 dice 函式、「/dice2/」執行 dice2 函式、「/dice3/」執行 dice3 函式、「/show/」執行 show 函式、「/filter/」執行 filter函式
這邊先把一些函式註解掉,之後要用到時再陸續加回來,以免稍後測試網頁會出現錯誤
from django.contrib import admin from django.urls import path from django.conf.urls import url from passdataapp.views import dice #,dice2,dice3,show,filter urlpatterns = [ path('admin/', admin.site.urls), url(r'^dice/$', dice), # url(r'^dice2/$', dice2), # url(r'^dice3/$', dice3), # url(r'^show/$', show), # url(r'^filter/$', filter), ]
範例1:傳遞變數到Template模板
首先請打開passdataapp裡的<view.py>寫一個名為dice的自訂函數
接下來將使用 randint()函式,寫出一個擲骰子點數的程序
from django.shortcuts import render import random def dice(request): no=random.randint(1,6) return render(request,"dice.html",{"no":no})
接著在templates目錄中創建一個<dice.html>模板,以{{no}}顯示字典
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>擲骰子</title> </head> <body> <h1>點數: {{no}}</h1> </body> </html>
打開瀏覽器,在網址列輸入127.0.0.1:8000/dice 即可看到剛剛寫的擲骰子網頁
※使用locals()函式
如果要傳遞的變數有很多,則可以在render函式中的第三個參數使用locals()函式
locals()函式可以將所有區域變數轉換成字典,這樣就能一次傳遞所有變數(global全域變數不會被處理)
以下將先做舉例說明:
def example(request): no=1 dict1={"name":ivan,"age":22} return render(request,"dice.html",locals())
經過locals()函式轉換後,會建立一個 {"no":1,"dict1":{"name":"ivan","age":22}} 的字典
※模板顯示變數(使用locals()函式)
在模板中要顯示locals()字典的內容,要需要用讀取字典的語法來讀取它
在模板中可以顯示接收的變數,必須以「變數」為鍵
{{變數}}
例如顯示 no
{{no}}
如果要顯示字典變數,前面要加上字典變數的名稱
{{字典變數.鍵}}
因此可以用下列語法取得name值的內容
{{dict1.name}}
如果要顯示串列變數,以「串列變數.索引」的格式取得
{{串列變數.索引}}
顯示list串列的第一個元素內容
{{list.0}}
範例2:使用locals()傳遞變數到Template模板 (一)
請打開passdataapp裡的<view.py>寫一個名為dice2的自訂函數
先使用 randint()函式,寫出三個擲骰子點數的程序
最後使用locals()函式傳遞所有區域變數
def dice2(request): no1=random.randint(1,6) no2=random.randint(1,6) no3=random.randint(1,6) return render(request,"dice2.html",locals())
接著在templates目錄中創建一個<dice2.html>模板,以{{no1}}、{{no2}}、{{no3}}顯示變數
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>擲骰子2</title> </head> <body> <h1>點數一: {{no1}}</h1> <h1>點數二: {{no2}}</h1> <h1>點數三: {{no3}}</h1> </body> </html>
最後記得在<views.py> import dice2 和在<urls.py>新增dice2的url路徑
最後網頁執行結果如下:
範例4:使用locals()傳遞變數到Template模板 (二)
請打開passdataapp裡的<view.py>寫一個名為times值為0的變數
接著自訂一個名為dice3的函數:
def dice3(request): global times #宣告全域變數 times = times + 1 local_times = times #指派給區域變數 username="Ivan" dict_no={"no":random.randint(1,6)} return render(request,"dice3.html",locals())
接著在templates目錄中創建一個<dice3.html>模板,以{{username}}、{{dict_no.no}}、{{local_times}}顯示變數
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>擲骰子3</title> </head> <body> <h1>玩家: {{username}}</h1> <h1>點數: {{dict_no.no}}</h1> <h1>累計次數: {{local_times}}</h1> </body> </html>
最後記得在<views.py> import dice3 和在<urls.py>新增dice3的url路徑
最後網頁執行結果如下:
三、Template語言:
Template模板有自己的語言,以下將做一些重點整理
名稱 | 說明 | 範例 |
變量
|
將views內容傳送到模板上 | {{username}} |
標籤 | if條件指令和for迴圈指令 |
{% if found %} {% for item in items %} |
單行註解
|
語法:{#註解文字#} |
{#這是註解文字#} |
多行註解 |
語法:{% comment %} ..... ..... {% endcomment %} |
{% comment %} 內容 內容 {% endcomment %} |
文字
|
HTML標籤或文字 | <title>這是標題</title> |
1.變量
變量就是要顯示的變數,可以是一般的變數,或是字典、屬性、方法、串列
以下將有一個對照表:
名稱 | Template語法 | Python語法 |
字典
|
{{dict.name}} | dict[name] |
屬性 | {{obj1.name}} |
obj1.name |
方法
|
{{obj1.show}} |
obj1.show() |
串列 |
{{list1.0}} |
list1[0] |
2.標籤
Template模板的條件判斷指令和迴圈指令稱為標籤,有if條件指令和for迴圈指令
(1)條件指令
分為單向、雙向、多向判斷式
單向判斷式:
{% if 條件 %} 程式區塊 {% endif %}
雙向判斷式:
{% if 條件 %} 程式區塊 {% else %} 程式區塊 {% endif %}
多向判斷式:
{% if 條件 %} 程式區塊 {% elseif %} 程式區塊 {% elseif %} 程式區塊 {% else %} 程式區塊 {% endif %}
(2)for迴圈指令
for迴圈可以依序讀取串列元素,並執行程式區塊。若串列無資料會執行 empty 區塊:
{% for 變數 in 串列 %} 程式區塊 {% empty %} 程式區塊 {% endfor %}
範例4:模板中接收串列資料並依序顯示
請打開passdataapp裡的<view.py>寫一個名為show的自訂函數:
將三個字典person1、person2、person3放進串列persons
def show(request): person1={"name":"Ivan","phone":"0911111111","age":22} person2={"name":"Clover","phone":"0922222222","age":20} person3={"name":"Victor","phone":"0933333333","age":21} persons=[person1,person2,person3] return render(request,"show.html",locals())
接著在templates目錄中創建一個<show.html>模板,以for迴圈顯示剛剛的資料
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>基本資料</title> </head> <body> <h2> {% for person in persons %} <ul> <li>姓名: {{person.name}}</li> <li>手機: {{person.phone}}</li> <li>年齡: {{person.age}}</li> </ul> {% empty%} 沒有任何資料 {% endfor %} </h2> </body> </html>
最後記得在<views.py> import show 和在<urls.py>新增show的url路徑
最後網頁執行結果如下:
(3)forloop變量及其屬性
Template的 for 迴圈提供 forloop 屬性,當作計數器
forloop | 說明 |
forloop.counter | 由1開始遞增迭代總數 |
forloop.counter0 | 由0開始遞增迭代總數 |
forloop.revcounter | 從串列元素總數開始遞減到1 |
forloop.revcounter0 | 從串列元素總數開始遞減到0 |
forloop.first | 判斷是否為第一次for迴圈,值為True或False |
forloop.last | 判斷是否為最後一次for迴圈,值為True或False |
forloop.parentloop | 上一層迴圈的forloop |
範例4延伸:模板中接收串列資料並依序顯示,由1開始顯示資料為第幾位員工
這邊增加了forloop屬性,記得語法為{{forloop屬性}}
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>基本資料</title> </head> <body> <h2> {% for person in persons %} 第 {{ forloop.counter}} 位員工 <ul> <li>姓名: {{person.name}}</li> <li>手機: {{person.phone}}</li> <li>年齡: {{person.age}}</li> </ul> {% empty%} 沒有任何資料 {% endfor %} </h2> </body> </html>
(4)for迴圈倒轉
將串列資料倒轉輸出有兩種方法:
- 在<views.py>視圖函式將取得的資料倒轉
- 在Template模板的for指令上加上reversed(例如:{% for person in persons reversed %})
※forloop屬性也要改為revcounter
3.過濾器
過濾器必須搭配變數一起使用,他使用「|」管線符號做處理
例如:cut可以移除變量中所有指定的字串{{values|cut:"2"}},則會把values字串中所有的2移除
過濾器種類實在太多了,需要參考的話可以直接點此連結參考官方的說明
接下來直接實行範例囉~
範例5:使用filter過濾器
請打開passdataapp裡的<view.py>寫一個名為filter的自訂函數:
def filter(request): value=3 list1=[2,3,4] pw="生番是你" html="<h1>hello</h1>" value2=False return render(request,"filter.html",locals())
接著在templates目錄中創建一個<filter.html>模板,開始測試各種過濾器吧!
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>過濾器測試</title> </head> <body> <h3>value={{value}},value|add:"2"= {{value|add:"2"}} <br /> <!-- 將值增加2 --> value|stringformat:"E"= {{value|stringformat:"E"}} <br /> <!-- 以科學記號標示 --> list1={{list1}},list1|length ={{list1|length}} <br /> <!-- 顯示串列和串列長度 --> {% if pw == "生番是你" %} <!-- 字串比對,==後面要空格--> 密碼正確 <br /> {% else %} 密碼錯誤 <br /> {% endif %} value2={{value2}},value2|yesno:"是,否,取消"= {{value2|yesno:"是,否,取消"}} <br /> <!-- 因為value2=False,所以會傳回第二個項目 --> html={{ html }},html|safe ={{ html|safe }} <!-- 以網頁格式顯示 --> </h3> </body> </html>
最後記得在<views.py> import filter 和在<urls.py>新增filter的url路徑
最後網頁執行結果如下:
留言列表