發(fā)表日期:2017-05 文章編輯:小燈 瀏覽次數(shù):2432
上一章中,你為你的項目創(chuàng)建了RESTful API。在本章中,你會學(xué)習(xí)以下知識點:
是時候把你的Django項目部署到生產(chǎn)環(huán)境了。我們將按以下步驟上線我們的項目:
uWSGI
和Ngnix
設(shè)置一個web服務(wù)器。在實際項目中,你可能需要處理多個環(huán)境。你最少會有一個本地環(huán)境和一個生產(chǎn)環(huán)境,但是很可能還有別的環(huán)境。有些項目設(shè)置是所有環(huán)境通用的,有些可能需要被每個環(huán)境覆蓋。讓我們?yōu)槎鄠€環(huán)境配置項目設(shè)置,同時保持項目的良好組織。
在educa
項目目錄中創(chuàng)建settings/
目錄。把項目的settings.py
文件移動到settings/
目錄中,并重命名為base.py
,然后在新目錄中創(chuàng)建以下文件結(jié)構(gòu):
settings/ __init__.py base.py local.py pro.py
這些文件分別是:
base.py
:包括通用和默認設(shè)置的基礎(chǔ)設(shè)置文件local.py
:你本地環(huán)境的自定義設(shè)置pro.py
:生產(chǎn)環(huán)境的自定義設(shè)置編輯settings/base.py
文件,找到這一行代碼:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
替換為下面這一行代碼:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(os.path.join(__file__, os.pardir))))
我們已經(jīng)把設(shè)置文件移動到了低一級的目錄中,所以我們需要BASE_DIR
正確的指向父目錄。我們使用os.pardir
指向父目錄。
編輯settings/local.py
文件,并添加以下代碼:
from .base import *DEBUG = TrueDATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
這是我們本地環(huán)境的設(shè)置文件。我們導(dǎo)入base.py
文件中定義的所有設(shè)置,只為這個生產(chǎn)環(huán)境定義特定設(shè)置。我們從base.py
文件中拷貝了DEBUG
和DATABASES
設(shè)置,因為每個環(huán)境會設(shè)置這些選項。你可以從base.py
文件中移除DEBUG
和DATABASES
設(shè)置。
編輯settings/pro.py
文件,并添加以下代碼:
from .base import *DEBUG = FalseADMINS = { ('Antonio M', 'email@mydomain.com'), }ALLOWED_HOSTS = ['educaproject.com', 'www.educaproject.com']DATABASES = { 'default': {} }
這些是生產(chǎn)環(huán)境的設(shè)置。它們分別是:
DEBUG
:設(shè)置DEBUG
為False
對任何生產(chǎn)環(huán)境都是強制的。不這么做會導(dǎo)致追蹤信息和敏感的配置數(shù)據(jù)暴露給每一個人。ADMINS
:當(dāng)DEBUG
為False
,并且一個視圖拋出異常時,所有信息會通過郵件發(fā)送給ADMINS
設(shè)置中列出的所有人。確保用你自己的信息替換上面的name/e-mail
元組。ALLOWED_HOST
:因為DEBUG
為False
,Django只允許這個列表中列出的主機為應(yīng)用提供服務(wù)。這是一個安全措施。我們包括了educaproject.com
和www.educaproject.com
域名,我們的網(wǎng)站會使用這兩個域名。DATABASES
:我們保留這個設(shè)置為空。我們將在下面討論生產(chǎn)環(huán)境的數(shù)據(jù)庫設(shè)置。處理多個環(huán)境時,創(chuàng)建一個基礎(chǔ)的設(shè)置文件,并為每個環(huán)境創(chuàng)建一個設(shè)置文件。環(huán)境設(shè)置文件應(yīng)用從通用設(shè)置繼承,并覆寫環(huán)境特定設(shè)置。
我們已經(jīng)把項目設(shè)置從默認的settings.py
文件放到了不同位置。除非你指定使用的設(shè)置模塊,否則不能用manage.py
工具執(zhí)行任何命令。在終端執(zhí)行管理命令時,你需要添加--settings
標(biāo)記,或者設(shè)置DJANGO_SETTINGS_MODULE
環(huán)境變量。打開終端執(zhí)行以下命令:
export DJANGO_SETTINGS_MODULE=educa.settings.pro
這會為當(dāng)前終端會話是設(shè)置DJANGO_SETTINGS_MODULE
環(huán)境變量。如果你不想為每個新終端都執(zhí)行這個命令,可以在.bashrc
或.bash_profile
文件中,把這個命令添加到你的終端配置。如果你不想設(shè)置這個變量,則必須使用--settings
標(biāo)記運行管理命令,比如:
python manage.py migrate --settings=educa.settings.pro
現(xiàn)在你已經(jīng)成功的為多個環(huán)境組織好了設(shè)置。
在本書中,我們一直使用SQLite數(shù)據(jù)庫。它的設(shè)置簡單快捷,但對于生產(chǎn)環(huán)境,你需要一個更強大的數(shù)據(jù)庫,比如PostgreSQL,MySQL或者Oracle。我們將在生產(chǎn)環(huán)境使用PostgreSQL。因為PostgreSQL提供的特性和性能,所以它是Django的推薦數(shù)據(jù)庫。Django還自帶django.contrib.postgres
包,允許你利用PostgreSQL的特定特性。你可以在這里閱讀更多關(guān)于這個模塊的信息。
如果你正在使用Linux,使用以下命令安裝PostgreSQL的依賴:
sudo apt-get install libpq-dev python-dev
然后使用以下命令安裝PostgreSQL:
sudo apt-get install postgresql postgresql-contrib
如果你正在使用Mac OS X或者Windows,你可以在這里下載PostgreSQL。
讓我們創(chuàng)建一個PostgreSQL用戶。打開終端,并執(zhí)行以下命令:
su postgres createuser -dP educa
會提示你輸入密碼和給予這個用戶的權(quán)限。輸入密碼和權(quán)限,然后使用以下命令創(chuàng)建一個新的數(shù)據(jù)庫:
createdb -E utf8 -U educa educa
然后編輯settings/pro.py
文件,并修改DATABASES
設(shè)置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'educa', 'USER': 'educa', 'PASSWORD': '****', } }
用你創(chuàng)建的數(shù)據(jù)庫名和用戶憑證替換上面的數(shù)據(jù)?,F(xiàn)在新數(shù)據(jù)庫是空的。執(zhí)行以下命令應(yīng)用所有數(shù)據(jù)庫遷移:
python manage.py migrate
最后使用以下命令創(chuàng)建一個超級用戶:
python manage.py createsuperuser
Django包括check
命令,可以在任何時候檢查你的項目。這個命令檢查Django項目中安裝的應(yīng)用,并輸出所有錯誤或警告。如果你包括了--deploy
選項,則只會觸發(fā)生成環(huán)境相關(guān)的額外檢查。打開終端,運行以下命令執(zhí)行一次檢查:
python manage.py check --deploy
你會看到?jīng)]有錯誤,但是有幾個警告的輸出。這意味著檢查成功了,但你應(yīng)該查看警告,看看是否可以做一些工作,讓你的項目在生產(chǎn)環(huán)境上是安全的。我們不會深入其中,你需要記住,在生產(chǎn)環(huán)境中使用之前,你應(yīng)該檢查項目所有相關(guān)的問題。
Django的主要部署平臺是WSGI。WSGI是Web Server Gateway Interface
的縮寫,它是在網(wǎng)絡(luò)上為Python應(yīng)用提供服務(wù)的標(biāo)準(zhǔn)。
當(dāng)你使用startproject
命令創(chuàng)建一個新項目時,Django會在項目目錄中創(chuàng)建一個wsgi.py
文件。這個文件包含一個WSGI應(yīng)用的可調(diào)用對象,它是你應(yīng)用的訪問點。使用Django開發(fā)服務(wù)器運行你的項目,以及在生產(chǎn)環(huán)境中用你選擇的服務(wù)器部署應(yīng)用都會使用WSGI。
你可以在這里進一步學(xué)習(xí)WSGI。
在這本書中,你一直使用Django開發(fā)服務(wù)器在本地環(huán)境運行項目。但是你需要一個實際的Web服務(wù)器在生產(chǎn)環(huán)境部署你的應(yīng)用。
uWSGI
是一個非常快速的Python應(yīng)用服務(wù)器。它使用WSGI規(guī)范與你的Python應(yīng)用通信。uWSGI
把Web請求轉(zhuǎn)換為Django項目可用處理的格式。
使用以下命令安裝uWSGI
:
pip install uwsgi
如果你正在使用Mac OS X,你可以使用brew install uwsgi
命令安裝uWSGI
。如果你想在Windows上安裝uWSGI
,則需要Cygwin。但是推薦你在基于Unix的環(huán)境中使用uWSGI
。
你可以從命令行中啟動uWSGI
。打開終端,在educa
項目目錄中執(zhí)行以下命令:
uwsgi --module=educa.wsgi:application \ --env=DJANGO_SETTINGS_MODULE=educa.settings.pro \ --http=127.0.0.1:80 \ --uid=1000 \ --virtualenv=/home/zenx/env/educa/
如果你沒有權(quán)限,則需要在命令前加上sudo
。
通過這個命令,我們用以下選項在本地運行uWSGI
:
educa.wsgi:application
作為WSGI的可調(diào)用對象。virtualenv
選項的路徑。如果你沒有使用虛擬環(huán)境,則跳過這個選項。如果你沒有在項目目錄下運行命令,則需要用你的項目目錄包括--chdir=/path/to/educa/
選項。
在瀏覽器中打開http://127.0.0.1:80/
。你會看到?jīng)]有加載CSS或者圖片的HTML。這是有道理的,因為我們還沒有配置uWSGI
為靜態(tài)文件提供服務(wù)。
uWSGI
允許你在.ini
文件中定義自定義配置。它比在命令行中傳遞選項更方便。在主educa/
目錄下創(chuàng)建以下文件結(jié)構(gòu):
config/ uwsgi.ini
編輯uwsgi.ini
文件,添加以下代碼:
[uwsgi] # variables projectname = educa base = /home/zenx/educa# configuration master = true virtualenv = /home/zenx/env/%(projectname) pythonpath = %(base) chdir = %(base) env = DJANGO_SETTINGS_MODULE=%(projectname).settings.pro module = educa.wsgi:application socket = /tmp/%(projectname).sock
我們定義了以下變量:
projectname
:你的Django項目名稱,這里是educa
。base
:educa
項目的絕對路徑。用你的絕對路徑替換它。還有一些會在uWSGI
選項中使用的自定義變量。你可以定義任意變量,只要它跟uWSGI
選項名不同就行。我們設(shè)置了以下選項:
master
:啟用主進程。virtualenv
:你的虛擬環(huán)境路徑。用響應(yīng)的路徑替換它。pythonpath
:添加到Python路徑的路徑。chdir
:項目目錄的路徑,加載應(yīng)用之前,uWSGI
改變到這個目錄。env
:環(huán)境變量。我們包括了DJANGO_SETTINGS_MODULE
變量,指向生產(chǎn)環(huán)境的設(shè)置。module
:使用的WSGI模塊。我們把它設(shè)置為application
可調(diào)用對象,它包含在項目的wsgi
模塊中。socket
:綁定到服務(wù)器的UNIX/TCP套接字。socket
選項用于與第三方路由(比如Nginx)通信,而http
選項用于uWGSI
接收傳入的HTTP請求,并自己進行路由。因為我們將配置Nginx作為Web服務(wù)器,并通過套接字與uWSGI
通信,所以我們將使用套接字運行uWSGI
。
你可以在這里找到所有可用的uWSGI
選項列表。
現(xiàn)在你可以使用自定義配置運行uWSGI
:
uwsgi --ini config/uwsgi.ini
因為uWSGI
通過套接字運行,所以你現(xiàn)在不能在瀏覽器中訪問uWSGI
實例。讓我們完成生產(chǎn)環(huán)境。
當(dāng)你為一個網(wǎng)站提供服務(wù)時,你必須為動態(tài)內(nèi)容提供服務(wù),同時還需要為靜態(tài)文件,比如CSS,JavaScript文件和圖片提供服務(wù)。雖然uWSGI
可以為靜態(tài)文件提供服務(wù),但它會在HTTP請求上增加不必要的開銷。因此,推薦在uWSGI
之前設(shè)置一個Web服務(wù)器(比如Nginx),為靜態(tài)文件提供服務(wù)。
Nginx是一個專注于高并發(fā),高性能和低內(nèi)存使用的Web服務(wù)器。Nginx還可以充當(dāng)反向代理,接收HTTP請求,并把它們路由到不同的后臺。通常情況下,你會在前端使用一個Web服務(wù)器(比如Nginx),高效快速的為靜態(tài)文件提供服務(wù),并且把動態(tài)請求轉(zhuǎn)發(fā)到uWSGI
的工作線程。通過使用Nginx,你還可以應(yīng)用規(guī)則,并從它的反向代理功能中獲益。
使用以下命令安裝Nginx:
sudo apt-get install nginx
如果你正在使用Mac OS X,你可以使用brew install nginx
命令安裝Nginx。你可以在這里找到Windows的二進制版本。
下圖展示了我們最終的生產(chǎn)環(huán)境:
當(dāng)客戶端瀏覽器發(fā)起一個HTTP請求時,會發(fā)生以下事情:
uWSGI
。uWSGI
把請求傳遞給Django處理。返回的HTTP響應(yīng)傳遞回Nginx,然后再返回到客戶端瀏覽器。在config/
目錄中創(chuàng)建nginx.conf
文件,并添加以下代碼:
# the upstream component nginx needs to connect to upstream educa { server unix:///tmp/educa.sock; }server { listen 80; server_name www.educaproject.com educaproject.com;location / { include /etc/nginx/uwsgi_params; uwsgi_pass educa; } }
這是Nginx的基礎(chǔ)配置。我們設(shè)置了一個名為educa
的上游(upstream),指向uWSGI
創(chuàng)建的套接字。我們使用server
指令,并添加以下配置:
www.educaproject.com
和educaproject.com
。Nginx會為來自這兩個域名的請求服務(wù)。/
路徑下的所有請求都路由到educa
套接字(uWSGI
)。我們還包括了Nginx自帶的默認uWSGI
配置參數(shù)。你可以在這里閱讀Nginx文檔。主要的Nginx配置文件位于/etc/nginx/nginx.conf
。它包括了/etc/nginx/sites-enabled/
下找到的所有配置文件。要讓Nginx加載你的自定義配置文件,需要如下創(chuàng)建一個符號鏈接:
sudo ln -s /home/zenx/educa/config/nginx.conf /etc/nginx/sites-enabled/educa.conf
用你項目的絕對路徑替換/home/zenx/educa/
。然后打開終端啟動uWSGI
:
uwsgi --ini config/uwsgi.ini
打開第二個終端,用以下命令啟動Nginx:
service nginx start
因為我們正在使用簡單的主機名,所以需要把它重定向到本機。編輯你的/etc/hosts
文件,添加下面兩行:
127.0.0.1 educaproject.com 127.0.0.1 www.educaproject.com
這樣,我們把兩個主機名路由到我們的本地服務(wù)器。在生產(chǎn)環(huán)境中你不需要這么用,因為你會在域名的DNS配置中把主機名指向你的服務(wù)器。
在瀏覽器中打開http://educaproject.com
。你可以看到你的網(wǎng)站,仍然沒有加載任何靜態(tài)資源。我們的生產(chǎn)環(huán)境馬上就好了。
為了最好的性能,我們將直接使用Nginx為靜態(tài)資源提供服務(wù)。
編輯settings/base.py
文件,并添加以下代碼:
STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
我們需要用Django導(dǎo)出靜態(tài)資源。collectstatic
命令從所有應(yīng)用中拷貝靜態(tài)文件,并把它們存儲到STATIC_ROOT
目錄中。打開終端執(zhí)行以下命令:
python manage.py collectstatic
你會看到以下輸出:
You have requested to collect static files at the destination location as specified in your settings:/educa/staticThis will overwrite existing files! Are you sure you want to do this?
輸入yes
讓Django拷貝這些文件。你會看到以下輸出:
78 static files copied to /educa/static
現(xiàn)在編輯config/nginx.conf
文件,并在server
指令中添加以下代碼:
location /static/ { alias /home/zenx/educa/static/; } location /media/ { alias /home/zenx/educa/media/; }
記得把/home/zenx/educa/
路徑替換為你項目目錄的絕對路徑。這些指令告訴Nginx,直接在/static/
或/media/
路徑下為靜態(tài)資源提供服務(wù)。
使用以下命令重新加載Nginx的配置:
server nginx reload
在瀏覽器中打開http://educaproject.com/
。現(xiàn)在你可以看到靜態(tài)文件了。我們成功的配置了Nginx來提供靜態(tài)文件。
SSL協(xié)議(Secure Sockets Layer)通過安全連接成為了為網(wǎng)站提供服務(wù)的標(biāo)準(zhǔn)。強烈鼓勵你在HTTPS下為網(wǎng)站提供服務(wù)。我們將在Nginx中配置一個SSL證書,安全的為我們網(wǎng)站提供服務(wù)。
在educa
項目目錄中創(chuàng)建ssl
目錄。然后使用以下命令生成一個SSL證書:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ssl/educa.key -out ssl/educa.crt
我們生成一個私有key和一個有效期是1年的2048個字節(jié)的證書。你將被要求輸入以下信息:
Country Name (2 letter code) [AU]: State or Province Name (full name) [Some-State]: Locality Name (eg, city) []: Madrid Organization Name (eg, company) [Internet Widgits Pty Ltd]: Zenx IT Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []: educaproject.com Email Address []: email@domain.com
你可以用自己的信息填寫要求的數(shù)據(jù)。最重要的字段是Common Name
。你必須制定證書的域名。我們將使用educaproject.com
。
這會在ssl/
目錄中生成educa.key
的私有key和實際證書educa.crt
文件。
編輯nginx.conf
文件,并修改server
指令,讓它包括以下SSL指令:
server { listen 80; listen 443 ssl; ssl_certificate /home/zenx/educa/ssl/educa.crt; ssl_certificate_key /home/zenx/educa/ssl/educa.key; server_name www.educaproject.com educaproject.com; # ...
現(xiàn)在我們的服務(wù)器在80端口監(jiān)聽HTTP,在443端口監(jiān)聽HTTPS。我們用ssl_certificate
制定SSL證書,用ssl_certificate_key
制定證書key。
使用以下命令重啟Nginx:
sudo service nginx restart
Nginx將會加載新的配置。在瀏覽器中打開http://educaproject.com
。你會看到一個類似這樣的靜態(tài)消息:
不同的瀏覽器,這個消息可能會不同。它警告你,你的網(wǎng)站沒有使用受信任的證書;瀏覽器不能驗證網(wǎng)站的身份。這是因為我們簽署了自己的證書,而不是從受信任的認證機構(gòu)獲得證書。當(dāng)你擁有真正的域名時,你可以申請受信任的CA為其頒發(fā)SSL證書,以便瀏覽器可以驗證其身份。
如果你想為真正域名獲得受信任的證書,你可以參數(shù)Linux Foundation創(chuàng)建的Let's Encrypt
項目。這是一個協(xié)作項目,目的是免費的簡化獲取和更新受信任的SSL證書。你可以在這里閱讀更多信息。
點擊Add Exception
按鈕,讓瀏覽器知道你信任這個證書。你會看到瀏覽器在URL旁顯示一個鎖的圖標(biāo),如下圖所示:
如果你點擊鎖圖標(biāo),則會顯示SSL證書的詳情。
Django包括一些SSL的特定設(shè)置。編輯settings/pro.py
設(shè)置文件,添加以下代碼:
SECURE_SSL_REDIRECT = True CSRF_COOKIE_SECURE = True
這些設(shè)置分別是:
SECURE_SSL_REDIRECT
:HTTP請求是否必須重定義到HTTPS請求CSRF_COOKIE_SECURE
:這必須為跨站點請求保護設(shè)置為建立一個安全的cookie非常棒!你已經(jīng)配置了一個生產(chǎn)環(huán)境,它會為你的項目提供高性能的服務(wù)。
你已經(jīng)了解了MIDDLEWARE
設(shè)置,其中包括項目的中間件。一個中間件是一個類,其中包括一些在全局執(zhí)行的特定方法。你可以把它看成一個低級別的插件系統(tǒng),允許你實現(xiàn)在請求或響應(yīng)過程中執(zhí)行的鉤子。每個中間件負責(zé)會在所有請求或響應(yīng)中執(zhí)行的一些特定操作。
避免在中間件中添加開銷昂貴的處理,因為它們?yōu)樵诿總€請求中執(zhí)行。
當(dāng)收到一個HTTP請求時,中間件會以MIDDLEWARE
設(shè)置中的出現(xiàn)順序執(zhí)行。當(dāng)一個HTTP響應(yīng)由Django生成時,中間件的方法會逆序執(zhí)行。
下圖展示了請求和響應(yīng)階段時,中間件方法的執(zhí)行順序。它還展示了可能被調(diào)用的中間件方法:
在請求階段,會執(zhí)行中間件的以下方法:
process_request(request)
:在Django決定執(zhí)行哪個視圖之前,在每個請求上調(diào)用。request
是一個HttpRequest
實例。process_view(request, view_func, view_args, view_kwargs)
:在Django調(diào)用視圖之前調(diào)用。它可以訪問視圖函數(shù)及其收到的參數(shù)在響應(yīng)階段,會執(zhí)行中間件的以下方法:
process_exception(request, exception)
:只有視圖函數(shù)拋出Exception
異常時才會調(diào)用。process_template_response(request, response)
:視圖執(zhí)行完成后調(diào)用,只有當(dāng)response
對象有render()
方法時才調(diào)用(比如它是TemplateResponse
或者等價對象)process_response(request, response)
:響應(yīng)返回到瀏覽器之前,在所有響應(yīng)上調(diào)用。因為中間件可以依賴之前已經(jīng)執(zhí)行的其它中間件方法在請求中設(shè)置的數(shù)據(jù),所以MIDDLEWARE
設(shè)置中的順序很重要。請注意,即使因為前一個中間件返回了HTTP響應(yīng),導(dǎo)致process_request()
和process_view()
被跳過,中間件的process_response()
方法也會被調(diào)用。這意味著process_response()
不能依賴于請求階段設(shè)置的數(shù)據(jù)。如果一個異常被某個中間件處理,并返回了一個響應(yīng),則之前的中間件類不會被調(diào)用。
當(dāng)添加新的中間件到
MIDDLEWARE
設(shè)置中時,確保把它放在了正確的位置。在請求階段,中間件方法按設(shè)置中的出現(xiàn)順序執(zhí)行,響應(yīng)階段則是逆序執(zhí)行。
你可以在這里查看更多關(guān)于中間件的信息。
我們將創(chuàng)建一個自定義的中間件,允許通過自定義子域名訪問課程。每個課程詳情視圖的URL(比如http://educaproject.com/courses/django/
)也可以通過子域名(用課程的slug
字段構(gòu)建)訪問,比如http://django.educaproject.com/
。
中間件可以位于項目的任何地方。但是,推薦的方式是在應(yīng)用目錄中創(chuàng)建一個middleware.py
文件。
在courses
應(yīng)用目錄中創(chuàng)建middleware.py
文件,并添加以下代碼:
from django.core.urlresolvers import reverse from django.shortcuts import get_object_or_404, redirect from .models import Courseclass SubdomainCourseMiddleware: def process_request(self, request): host_parts = request.get_host().split('.') if len(host_parts) > 2 and host_parts[0] != 'www': # get course for the given subdomain course = get_object_or_404(Course, slug=host_parts[0]) course_url = reverse('course_detail', args=[course.slug]) # redirect current request to the course_detail view url = '{}://{}{}'.format( request.scheme, '.'.join(host_parts[1:]), course_url ) return redirect(url)
我們創(chuàng)建了一個實現(xiàn)了process_request()
的中間件。當(dāng)收到HTTP請求時,我們執(zhí)行以下任務(wù):
mycourse.educaproject.com
,則會生成['mycourse', 'educaproject', 'com']
列表。www
,則嘗試使用子域名提供的slug
獲得課程。Http 404
異常。否則,我們使用主域名重定向到課程詳情的URL。編輯項目的settings/base.py
文件,在MIDDLEWARE
設(shè)置底部添加courses.middleware.SubdomainCourseMiddleware
:
MIDDLEWARE = [ # ... 'courses.middleware.SubdomainCourseMiddleware', ]
現(xiàn)在我們的中間件會在每個請求上執(zhí)行。
我們需要Nginx為帶任意可能子域名的我們的網(wǎng)站提供服務(wù)。編輯config/nginx.conf
文件,找到這一行代碼:
server_name www.educaproject.com educaproject.com;
替換為下面這一行代碼:
server_name *.educaproject.com educaproject.com;
通過使用星號,這條規(guī)則會應(yīng)用與educaproject.com
的所有子域名。為了在本地測試我們的中間件,我們需要在/etc/hosts
中添加想要測試的子域名。要用別名為django
的Course
對象測試中間件,需要在/etc/hosts
文件添加這一行:
127.0.0.1 django.educaproject.com
然后在瀏覽器中打開https://django.educaproject.com/
。中間件會通過子域名找到課程,并重定向到https://educaproject.com/course/django/
。
Django允許你的應(yīng)用為manage.py
工具注冊自定義管理命令。例如,我們在第九章使用makemessages
和compilemessages
管理命令來創(chuàng)建和編譯轉(zhuǎn)換文件。
一個管理命令由一個Python模塊組成,其中Python模塊包括一個從django.core.management.BaseCommand
繼承的Command
類。你可以創(chuàng)建簡單命令,或者讓它們接收位置和可選參數(shù)作為輸入。
Django在INSTALLED_APPS
設(shè)置中激活的每個應(yīng)用的management/commands/
目錄中查找管理命令。發(fā)現(xiàn)的每個模塊注冊為以其命名的管理命令。
你可以在這里進一步學(xué)習(xí)自定義管理命令。
我們將注冊一個自定義管理命令,提供學(xué)生至少報名一個課程。該命令會給注冊時間長于指定時間,但尚未報名任何課程的用戶發(fā)送一封提醒郵件。
在students
應(yīng)用目錄中創(chuàng)建以下文件結(jié)構(gòu):
management/ __init__.py commands/ __init__.py enroll_reminder.py
編輯enroll_reminder.py
文件,并添加以下代碼:
import datetime from django.conf import settings from django.core.management.base import BaseCommand from django.core.mail import send_mass_mail from django.contrib.auth.models import User from django.db.models import Countclass Command(BaseCommand): help = 'Send an e-mail reminder to users registered more \ than N days that are not enrolled into any courses yet'def add_arguments(self, parser): parser.add_argument('--days', dest='days', typy=int)def handle(self, *args, **kwargs): emails = [] subject = 'Enroll in a course' date_joined = datetime.date.today() - datetime.timedelta(days=options['days']) users = User.objects.annotate( course_count=Count('courses_enrolled') ).filter( course_count=0, date_joined__lte=date_joined ) for user in users: message = 'Dear {},\n\nWe noticed that you didn\'t enroll in any courses yet.'.format(user.first_name) emails.append(( subject, message, settings.DEFAULT_FROM_EMAIL, [user.email] )) send_mass_mail(emails) self.stdout.write('Sent {} reminders' % len(emails))
這是我們的enroll_reminder
命令。這段代碼完成以下任務(wù):
Command
類從BaseCommand
繼承。help
屬性。該屬性為命令提供了一個簡單描述,如果你執(zhí)行python manage.py help enroll_reminder
命令,則會打印這個描述。add_arguments()
方法添加--days
命名參數(shù)。該參數(shù)用于指定用戶注冊了,但沒有報名參加任何課程,從而需要接收提醒郵件的最小天數(shù)。handle()
方法包括實際命令。我們從命令行解析中獲得days
屬性。我們檢索注冊天數(shù)超過指定天數(shù),當(dāng)仍沒有參加任何課程的用戶。我們用一個用戶報名參加的總課程數(shù)量注解(annotate)QuerySet實現(xiàn)此目的。我們?yōu)槊總€用戶生成一封提醒郵件,并把它添加到emails
列表中。最后,我們用send_mass_mail()
函數(shù)發(fā)送郵件,這個函數(shù)打開單個SMTP連接發(fā)送所有郵件,而不是每發(fā)送一封郵件打開一個連接。你已經(jīng)創(chuàng)建了第一個管理命令。打開終端執(zhí)行你的命令:
python manage.py enroll_reminder --days=20
如果你沒有正在運行的本地SMTP服務(wù)器,你可以參考第二章,我們?yōu)榈谝粋€Django項目配置了SMTP設(shè)置。另外,你可以添加以下行到settings/local.py
文件,讓Django在開發(fā)期間輸出郵件到標(biāo)準(zhǔn)輸出:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
讓我們調(diào)度管理命令,讓服務(wù)器沒有早上8點運行它。如果你正在使用基于Unix的系統(tǒng),比如Linux或者Mac OS X,打開終端執(zhí)行crontab -e
來編輯計劃任務(wù)。在其中添加下面這一行:
0 8 * * * python /path/to/educa/manage.py enroll_reminder --days=20 --settings=educa.settings.pro
如果你不熟悉Cron
,你可以在這里學(xué)習(xí)它。
如果你正在使用Windows,你可以使用Task scheduler
調(diào)度任務(wù)。你可以在這里進一步學(xué)習(xí)它。
定期執(zhí)行操作的另一個方法是用Celery創(chuàng)建和調(diào)度任務(wù)。記住,我們在第七章使用Celery執(zhí)行了異步任務(wù)。除了使用Cron
創(chuàng)建和調(diào)用管理命令,你還可以使用Celery beat scheduler
創(chuàng)建異步任務(wù)并執(zhí)行它們。你可以在這里進一步學(xué)習(xí)使用Celery調(diào)度定時任務(wù)。
對要使用
Cron
或者Windows調(diào)度任務(wù)控制面板調(diào)度的獨立腳本使用管理命令。
Django還包括一個用Python調(diào)用管理命令的工具。你可以在代碼中如下執(zhí)行管理命令:
from django.core import management management.call_command('enroll_reminder', days=20)
恭喜你!現(xiàn)在你已經(jīng)為你的應(yīng)用創(chuàng)建了自定義管理命令,并在需要時調(diào)度它們。
在這一章中,你使用uWSGI
和Nginx
配置了一個生產(chǎn)環(huán)境。你還實現(xiàn)了一個自定義中間件,并學(xué)習(xí)了如何創(chuàng)建自定義管理命令。
日期:2018-04 瀏覽次數(shù):6927
日期:2017-02 瀏覽次數(shù):3623
日期:2017-09 瀏覽次數(shù):3890
日期:2017-12 瀏覽次數(shù):3696
日期:2018-12 瀏覽次數(shù):5046
日期:2016-12 瀏覽次數(shù):4750
日期:2017-07 瀏覽次數(shù):13811
日期:2017-12 瀏覽次數(shù):3674
日期:2018-06 瀏覽次數(shù):4436
日期:2018-05 瀏覽次數(shù):4612
日期:2017-12 瀏覽次數(shù):3718
日期:2017-06 瀏覽次數(shù):4135
日期:2018-01 瀏覽次數(shù):4118
日期:2016-12 瀏覽次數(shù):4075
日期:2018-08 瀏覽次數(shù):4570
日期:2017-12 瀏覽次數(shù):3911
日期:2016-09 瀏覽次數(shù):6668
日期:2018-07 瀏覽次數(shù):3359
日期:2016-12 瀏覽次數(shù):3397
日期:2018-10 瀏覽次數(shù):3528
日期:2018-10 瀏覽次數(shù):3647
日期:2018-09 瀏覽次數(shù):3744
日期:2018-02 瀏覽次數(shù):3762
日期:2015-05 瀏覽次數(shù):3679
日期:2018-09 瀏覽次數(shù):3457
日期:2018-06 瀏覽次數(shù):3587
日期:2017-02 瀏覽次數(shù):4029
日期:2018-02 瀏覽次數(shù):4509
日期:2018-02 瀏覽次數(shù):4369
日期:2016-12 瀏覽次數(shù):3718
Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.