"Couldn't import Django. Are you sure it's installed and "
可見它主要是從 django 套件的核心引入 execute_from_command_line 模組.
其中 settings.py 與 urls.py 是專案的主角, __init.py__ 是一個空檔案, 目的只是要讓下層專案目錄符合套件形式上的要求而設; wsgi.py 則只有部署到主機上時才會用到.
The `urlpatterns` list routes URLs to views. For more information please see:
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
Django settings for mysite project.
Generated by 'django-admin startproject' using Django 2.1.7.
For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/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.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '2dwazqn8dsi#9lae!c32%fv(*rk@@ygbwg0c40+d38vb(+oky@'
# 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',
]
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 = 'mysite.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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 = 'mysite.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.1/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.1/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.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_URL = '/static/'
三. 用 HttpResponse 輸出 Hello World 頁面 :
如上所述, Django 是利用 urls.py 指配客戶端要求之 URL 至 views.py 的處理函數以產生回應之網頁, 以下以經典的輸出 Hello World 的網頁為例進行測試. 首先在下層專案目錄 (第二層 mysite) 下新增一個 views.py 如下 :
#views.py
from django.http import HttpResponse
def
helloworld(request):
return HttpResponse('<b>Hello World! <i>您好</i></b>')
此程式從 django.http 模組引入 HttpResponse 類別以處理 HTTP 回應, 然後定義一個自訂函數 helloworld() 來回應客戶端, 此函數有一個傳入參數 request 會從伺服器接收一個封裝了客戶端要求的 HttepRequest 物件, 雖然此例用不到還是要傳入, 因為這是必要參數. 此函數只是簡單地呼叫 HttpResponse() 物件方法傳回字串. 注意, 由於回應中含有中文, 因此須以 utf-8 格式存檔.
接著編輯下層專案目錄下的 urls.py, 先從 mysite.views 模組匯入 helloworld 函數, 然後在 urlpatterns 串列中, 於預設的 admin 底下增加一個 path() 函數呼叫, 將要求之 URL 字串 'hello/' 以及所對應之 helloworld 函數當作參數傳進去 :
#urls.py
from django.contrib import admin
from django.urls import path
from mysite.views import helloworld
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/', helloworld),
]
路由器 urls.py 也可以用下列寫法 :
#urls.py
from django.contrib import admin
from django.urls import path
from mysite import views
#或
from . imports views 亦可
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/',
views.helloworld),
]
差異主要是因為 import 的方式不同, 如果是匯入 views 模組, 則在 path() 內配對的目的地就必須冠上views 模組名稱.
這樣就完成網站更新, Django 開發伺服器會偵測到組態更新, 不須重開伺服器, 於瀏覽器網址列輸入 localhost:8000/hello 就會顯示如下網頁 :
Bingo! 以上便是 Django 輸出簡單網頁的做法.
上面的例子是將欲輸出的網頁直接傳給 HttpResonse(), 如果是較長的網頁內容 views.py 裡面應改用三個引號的長字串變數較易讀, 例如將上面的 views.py 改為如下效果一樣 (路由器 urls.py 不用改) :
#views.py
from django.http import HttpResponse
def helloworld(request):
html='''
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>
</head>
<body>
<b>Hello World! <i>您好</i></b>
</body>
</html>
'''
return HttpResponse(
html)
有了 /hello 這個 URL 後雖然輸入 localhost:8000/hello 會顯示正確的網頁輸出, 但若請求根目錄卻顯示 404 page not found :
這是因為在 urls.py 與對應之 views.py 裡面沒有處理根目錄請求之故. 首先在 views.py 中添加名為 home 的函數來輸出根目錄要求之輸出 :
#views.py
from django.http import HttpResponse
def helloworld(request):
return HttpResponse('<b>Hello World! <i>您好</i></b>')
def home(request):
return HttpResponse('<b>歡迎來到我的首頁!</b>')
函數 home() 只是簡單地輸出一個字串而已. 然後修改 urls.py 如下 :
#urls.py
from django.contrib import admin
from django.urls import path
from mysite.views import home, helloworld
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/', helloworld),
path('', home),
]
在 urlpatterns 串列中添加空字串 (表示根目錄) 對應到 views.py 裡面的 home() 函數, 這樣在網址列輸入 localhost:8000 就會顯示 '
歡迎來到我的首頁!' 了.
上面的範例處理了路由問題, 但能否傳遞參數呢? 可以的, Django 依循 RESTful 方式傳遞參數, 亦即參數放在 URL 的後面, 格式如下 :
route/param
例如向 Tony 打招呼的 URL 是 /hello/Tony.
在上面已提及, 在 Django 2 的路由器 urls.py 程式中, 從伺服器接收參數的方式已改為使用尖括號並同時完成資料型態轉換, 格式如下 :
path('route/
<type:param>/', function_name),
將上面的 urls.py 改為如下 :
#urls.py
from django.contrib import admin
from django.urls import path
from mysite.views import home, helloworld
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/
<str:name>/', helloworld),
path('', home),
]
此處尖括號內的 str:name 表示以字串格式接收 URL 中的參數, 名稱為 name. 注意, 若無指定資料型態, 預設為字串, 參數的型態格式有下列 5 種, 稱為路徑轉換器 (path converter) :
參數型態 | 說明 |
int | 0 或正整數 |
str | 非空字串 (預設), 不含路徑分隔字元 "/" |
slug | 以 ASCII 字元組成之字串 (含 dash 與 underscore) |
uuid | 通用唯一標示碼 uuid 字串 (字母皆小寫) |
path | 非空字串 (含路徑分隔字元 "/"), 可表示完整的 URL |
參考 :
https://docs.djangoproject.com/en/3.0/topics/http/urls/
因為參數型態預設為 str, 所以上面範例 urls.py 中的 :str 也可以不寫. 而在 views.py 中, 參數 name 則放在第一參數 request 後面, 例如 :
#views.py
from django.http import HttpResponse
def helloworld(request,
name):
return HttpResponse('<b>Hello World! </b>' +
name)
def home(request):
return HttpResponse('<b>歡迎來到我的首頁!</b>')
此處將接收到的第二參數 name 串在 HTML 碼後面回應給客戶端, 在瀏覽器網址列上輸入 localhost:8000/hello/Tony 結果如下 :
可見參數 name=Tony 已被成功地透過 URL 傳遞給伺服器並回應給客戶端. 如果要傳遞多個參數, 則這些參數就在 URL 中串接在路由的後面, 格式為 :
route/param1/param2/....
在 urls.py 的 path() 呼叫中, 依序用倒斜線串接的尖括號來接收這些參數 (可指定轉換成所要之資料型態), 例如 :
#urls.py
from django.contrib import admin
from django.urls import path
from mysite.views import home, helloworld
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/
<name>/
<message>/', helloworld),
path('', home),
]
而 views.py 中所對應的處理函數要依序接收所傳進來的參數, 例如 :
#views.py
from django.http import HttpResponse
def helloworld(request,
name, message):
return HttpResponse('<b>Hello World! </b>' +
name + ': ' +
message)
def home(request):
return HttpResponse('<b>歡迎來到我的首頁!</b>')
這時在網址列輸入 localhost:8000/tony/How are you 會看到如下網頁 :
視圖處理函數 views.py 中的傳入參數也可以有預設值, 當請求的 URL 中沒有傳參數過來時就會以預設值頂替, 範例如下 :
#urls.py
from django.contrib import admin
from django.urls import path
from mysite.views import home, helloworld
urlpatterns = [
path('admin/', admin.site.urls),
path('
hello/<name>/', helloworld),
path('hello/<name>/<message>/', helloworld),
path('', home),
]
此處 urls.py 需添加 'hello/<name>/' 這一筆, 否則會找不到 URL 而報錯, 並不會因為有 'hello/<name>/<message>/' 這筆而找到路由 (因為不是用正規式). 相對應的 views.py 中 helloworld() 的 message 要設定預設值如下 :
#views.py
from django.http import HttpResponse
def helloworld(request, name,
message='Blablabla ...'):
return HttpResponse('<b>Hello World! </b>' + name + ': ' + message)
def home(request):
return HttpResponse('<b>歡迎來到我的首頁!</b>')
注意, Python 函數的傳入參數若有
預設值必須全部靠右放置 (即通通放在最後面), 否則執行時會報錯. 在瀏覽器網址列輸入 localhost:8000/tony/ 沒有傳入 message 參數便會以其預設值 "blablabla" 代替 :
以上便是 Django 對 HTTP 請求與回應之基本處理方式.
四. HttpRequest 物件 :
Django 的請求處理流程是從 settings.py 開始. 當啟動 Django 內建的開發伺服器時, 它會讀取專案目錄下的 settings.py 以解析網站的設定資訊, 其中最重要的是 ROOT_URLCONF, 它指到一個根路由設定檔, 預設為 urls.py, 此檔案告訴 Django 在此網站中會用到哪些 Python 模組以及 URL 對應到 views.py 裡哪些函數.
當 Django 接到頁面請求時, 它會將 HTTP 請求之 metadata 打包成 HttpRequest 物件, 然後在 urls.py 中依序搜尋 URL 樣式, 當找到第一個符合的 pattern 時以 HttpRequest 物件為第一參數呼叫 views.py 中對應的函數, 此函數處理完需傳回一個 HttpResponse 物件, Django 會將其轉成 HTTP 回應給用戶端.
HttpRequest 物件的常用屬性如下 :
HttpRequest 物件屬性 | 說明 |
method | 請求的方法 (字串), 例如 "GET", "POST" |
GET | GET 方法所帶之參數 (字典) |
POST | POST 方法所帶之參數 (字典) |
user | 使用者物件, 可用 is_authenticated() 方法判斷是否登入 |
session | 連線物件 (字典) |
COOKIES | Cookies 物件 (字典) |
META | 請求之標頭資訊 (字典), 例如 HTTP_REFERER 等 |
其中 method 可用來判斷不同請求方法的處理方式, 例如 :
if request.method=="GET":
....
elif request.method=="POST"
屬性 user 可用來判斷使用者是否已登入 :
if request.user.is_authenticated():
...
else:
...
詳細參考 :
#
https://docs.djangoproject.com/en/2.1/ref/request-response/
例如在 views.py 增加一個 request_test() 的視圖函數 :
#views.py
from django.http import HttpResponse
def helloworld(request, name, message='Blablabla ...'):
return HttpResponse('<b>Hello World! </b>' + name + ': ' + message)
def home(request):
return HttpResponse('<b>歡迎來到我的首頁!</b>')
def request_test(request):
try:
method=request.method
http_host=request.META['HTTP_HOST']
http_user_agent=request.META['HTTP_USER_AGENT']
remote_addr=request.META['REMOTE_ADDR']
return HttpResponse('[method]:%s<br>[http_host]:%s<br>\
[http_user_agent]:%s<br>[remote_addr]:%s'\
%(method, http_host, http_user_agent, remote_addr))
except e:
return HttpResponse('Error:%s' &e)
然後在 urls.py 裡面添加一個 request_test 路徑對應 :
#urls.py
from django.contrib import admin
from django.urls import path
from mysite.views import home, helloworld,
request_test
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/<name>/', helloworld),
path('hello/<name>/<message>/', helloworld),
path('', home),
path('request_test/', request_test),
]
以瀏覽器拜訪 localhost:8000/request_test/ 結果如下 :
五. HttpResponse 物件 :
HttpRequest 物件是 Django 自動建立的, 但 HttpResponse 物件則需自行呼叫 HttpResponse() 建構子或 render(), render_to_response() 函數, 並傳入一個表示回應頁面內容的 HTML 字串來建立. 每一個 views.py 內的函數都必須傳回一個 HttpResponse 物件以便讓客戶端能看到回應結果.
HttpResponse 物件也可以用檔案串流方式操作, 先建立 HttpResponse 物件, 然後再分批呼叫其 write() 方法寫入串流後再傳回用戶端. 這種方法對設定回應標題很方便.
上面範例中 views.py 裡的 request_test() 也可以這麼寫 :
#views.py
from django.http import HttpResponse
def helloworld(request, name, message='Blablabla ...'):
return HttpResponse('<b>Hello World! </b>' + name + ': ' + message)
def home(request):
return HttpResponse('<b>歡迎來到我的首頁!</b>')
def request_test(request):
response=HttpResponse()
try:
method=request.method
http_host=request.META['HTTP_HOST']
http_user_agent=request.META['HTTP_USER_AGENT']
remote_addr=request.META['REMOTE_ADDR']
response.
write('[method]:%s<br>' % (method))
response.
write('[http_host]:%s<br>' % (http_host))
response.
write('[http_user_agent]:%s<br>' % (http_user_agent))
response.
write('[remote_addr]:%s' % (remote_addr))
response['Cache-Control']='no-cache' #設定回應標題
return
response
except e:
return response.write('Error:%s' % e)
此例設定回應標題 Cache-Control 為 no-cache, 這可以在 Chrome 按 F12 進入開發者模式, 在 Network/Headers 頁籤內看到 :
HttpResponse 類別也定義了一些好用的子類別, 方便處理重導向或回應 404, 500 等錯誤, 常用 HttpResponse 子類別如下表 :
HttpResponse 子類別 | 說明 |
HttpResponsePermanentRedirect | 永久重導向至指定 URL (狀態碼 301) |
HttpResponseRedirect | 重導向至指定 URL (狀態碼 302) |
HttpResponseBadRequest | 請求錯誤 (狀態碼 400) |
HttpResponseForbidden | 頁面不存在 (狀態碼 403) |
HttpResponseNotFound | 頁面禁止 (狀態碼 404) |
HttpResponseNotAllowed | 請求不允許 (狀態碼 405) |
HttpResponseServerError | (狀態碼 500) |
例如下列重導向範例是在上例的 views.oy 中添加一個 redirect() 函數, 直接傳回 HttpResponseRedirect 物件重導向首頁 :
#views.py
from django.http import HttpResponse
from django.http import HttpResponseRedirect
def helloworld(request, name, message='Blablabla ...'):
return HttpResponse('<b>Hello World! </b>' + name + ': ' + message)
def home(request):
return HttpResponse('<b>歡迎來到我的首頁!</b>')
def request_test(request):
response=HttpResponse()
try:
method=request.method
http_host=request.META['HTTP_HOST']
http_user_agent=request.META['HTTP_USER_AGENT']
remote_addr=request.META['REMOTE_ADDR']
response.write('[method]:%s<br>' % (method))
response.write('[http_host]:%s<br>' % (http_host))
response.write('[http_user_agent]:%s<br>' % (http_user_agent))
response.write('[remote_addr]:%s' % (remote_addr))
response['Cache-Control']='no-cache'
return response
except e:
return response.write('Error:%s' % e)
def redirect(request):
return HttpResponseRedirect("/")
然後在 urls.py 中匯入此 redirect() 函數並對應到 "redirect/" :
#urls.py
from django.contrib import admin
from django.urls import path
from mysite.views import home, helloworld, request_test
from mysite.views import redirect
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/<name>/', helloworld),
path('hello/<name>/<message>/', helloworld),
path('', home),
path('request_test/', request_test),
path('redirect/', redirect),
]
在網址列輸入 localhost:8000/redirect 果真重導向至首頁.
2020-01-20 補充 :
Django 在 2020 年已推出第三版, 在設定 (例如語言) 上與第二版有些差異, 第二版最後一個版本是 2.2.9 版, 參考 :
#
What Python version can I use with Django?