本章包含如下主題:

使用虛擬環(huán)境創(chuàng)建項目文件結(jié)構(gòu)通過pip處理項目依賴為開發(fā)、測試、預(yù)發(fā)布和生產(chǎn)環(huán)境配置設(shè)置在設(shè)" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > Django 3網(wǎng)頁開發(fā)指南第4版 第1章 Django 3.0入門

Django 3網(wǎng)頁開發(fā)指南第4版 第1章 Django 3.0入門

時間:2023-05-29 01:36:01 | 來源:網(wǎng)站運營

時間:2023-05-29 01:36:01 來源:網(wǎng)站運營

Django 3網(wǎng)頁開發(fā)指南第4版 第1章 Django 3.0入門:本文完整目錄請見 Django 3網(wǎng)頁開發(fā)指南 - 第4版

本章包含如下主題:

引言

本章中我們將通過幾個實例來使用Python 3通過Django 3.0開啟新的項目。我們選取了最有效的方法來處理可擴展項目布局、配置,使用virtualenv或Docker來管理項目皆可。

讀者應(yīng)當(dāng)已熟悉Django基礎(chǔ)知識、Git版本控制、MySQL和PostgreSQL數(shù)據(jù)以及命令行的使用。應(yīng)當(dāng)使用基于Unix的操作系統(tǒng),如macOS或Linux。使用基于Unix的系統(tǒng)平臺來開發(fā)Django會更有意義,因為大多情況下項目會在Linux服務(wù)器上進行發(fā)布,通過這種方式不論對開發(fā)還是部署都可以建立日常的工作習(xí)慣。如果讀者使用Windows來在本地開發(fā),很多工作方式相同,但同時會存在一些差別。

不論在什么本地平臺中的開發(fā)環(huán)境中使用Docker,都可以通過部署提升應(yīng)用的可移植性,因為Docker容器的環(huán)境可以與部署服務(wù)器保持精準(zhǔn)一致。還應(yīng)說明不論是否使用 Docker 來進行開發(fā),本章中的各小節(jié)都要求讀者在本地機器上安裝了相應(yīng)的版本控制系統(tǒng)和數(shù)據(jù)庫服務(wù)。

技術(shù)要求

使用本書中的代碼,讀者需要有最新穩(wěn)定版本的Python,可通過https://www.python.org/downloads/進行下載。在編寫本書時,最新版本為3.8.X.。還需要有MySQL或PostgreSQL數(shù)據(jù)庫??梢酝ㄟ^https://dev.mysql.com/downloads/下載MySQL數(shù)據(jù)庫服務(wù),通過https://www.postgresql.org/download/下載PostgreSQL數(shù)據(jù)庫服務(wù)。其它要求會在具體小節(jié)中說明。

第一章的所有代碼可在 GitHub倉庫的Chapter01目錄中進行查看。

使用虛擬環(huán)境

你很有可能會在自己的電腦上開發(fā)多個Django項目。有些模塊如 virtualenv、setuptools、wheel或Ansible可以只安裝一次并在所有項目中共享。另外一些模塊,如Django、第三方Python庫和Django應(yīng)用則需要相互分離。virtualenv工具是一個可以將不同Python項目進行分離成相互獨立環(huán)境的工具。本小節(jié)中我們就來學(xué)習(xí)如何使用它。

準(zhǔn)備工作

管理Python包需要使用pip。如果讀者使用的是Python 3.4以上版本,在安裝Python時就已經(jīng)自帶了。如果使用的是其它版本的Python,需要根據(jù)https://pip.readthedocs.io/en/stable/installing/中的安裝指南執(zhí)行pip的安裝。下面我們來升級共享Python模塊、pip、setuptools和wheel:

$ sudo pip3 install --upgrade pip setuptools wheel虛擬環(huán)境從Python 3.3開始就已進行內(nèi)置了。

如何實現(xiàn)...

一旦安裝了所要求的軟件,選擇一個位置(如家目錄)保存所有的Django項目。在創(chuàng)建好目錄后執(zhí)行如下步驟:

  1. 進入新建的目錄、創(chuàng)建使用共享系統(tǒng)級包的虛擬環(huán)境:
$ cd ~/projects $ mkdir myproject_website 、 $ cd myproject_website $ python3 -m venv env
  1. 要使用新建的虛擬環(huán)境,需要在當(dāng)前shell中啟動激活腳本??赏ㄟ^如下命令來執(zhí)行:
$ source env/bin/activate
  1. 根據(jù)所使用的shell的不同,可能會無法使用 source命令。另一種方式是通過如下命令,可以實現(xiàn)同樣的效果(注意在點號和env中間有一個空格) :
$ . env/bin/activate
  1. 這時可以看到命令行提示符中有一個項目名稱的前綴,如下所示:
(env)$
  1. 要退出虛擬環(huán)境,使用如下命令:
(env)$ deactivate

實現(xiàn)原理...

在創(chuàng)建虛擬環(huán)境時,會創(chuàng)建一些特定的目錄(bin, include及l(fā)ib) 來存儲Python安裝版本的拷貝,還會定義一些共享Python路徑。在啟用虛擬環(huán)境后,通過pip或easy_install安裝的包都會放到虛擬環(huán)境的站點包中以供使用,而不會放到原來安裝的Python的全局站點包中。

要在虛擬環(huán)境中安裝Django 3.0.x,輸入如下命令:

(env)$ pip install "Django~=3.0.0"

其它內(nèi)容

創(chuàng)建項目文件結(jié)構(gòu)

項目中連貫性的文件結(jié)構(gòu)可以讓組織上更清晰、也更具生產(chǎn)力。在定義了基礎(chǔ)工作流時,可以更快速地將精力放在業(yè)務(wù)邏輯上、創(chuàng)建優(yōu)秀的項目。

準(zhǔn)備工作

如果還沒照著前面操作,請創(chuàng)建一個~/projects目錄,在其中存放所有的Django項目(在使用虛擬環(huán)境一節(jié)中有相應(yīng)的說明)。

然后為具體的項目創(chuàng)建一個目錄,例如myproject_website。啟動env目錄中的虛擬環(huán)境。像前一小節(jié)中那樣激活環(huán)境并安裝Django。我們推薦為與項目相關(guān)的shell腳本添加一個commands目錄,為數(shù)據(jù)庫導(dǎo)出創(chuàng)建一個db_backups目錄,為網(wǎng)站設(shè)計文件添加一個mockups目錄,最重要的是為Django項目添加一個src目錄。

如何實現(xiàn)...

按照如下步驟來為項目創(chuàng)建文件結(jié)構(gòu):

  1. 先啟動虛擬環(huán)境,進入src目錄啟動一個新的Django項目,如下:
(env)$ django-admin.py startproject myproject所執(zhí)行命令會創(chuàng)建一個名為myproject的目錄,其中存放項目文件。目錄中包含一個名稱同樣為myproject的Python模塊。為避免混淆、保持方便,我們將頂級目錄重命名為django-myproject。這個目錄會進行版本控制,其中應(yīng)包含.git或相應(yīng)的子目錄。
  1. 在django-myproject目錄中,創(chuàng)建一個README.md文件來描述通過django-admin.py startproject myproject所創(chuàng)建的項目。
  2. django-myproject目錄中還將包含如下內(nèi)容:
  1. 在項目的根目錄django-myproject,創(chuàng)建如下目錄
  1. myproject目錄中包含如下目錄和文件:
  1. 在site_static目錄中,為具體站點靜態(tài)文件創(chuàng)建site目錄來作為命名空間。然后,我們在其中分類的子目錄之前分割靜態(tài)文件。例如,像下面這樣:
  1. 除site目錄外,site_static中也可能包含重寫的第三方應(yīng)用的靜態(tài)文件目錄,例如,可能會包含重寫Django CMS中靜態(tài)文件的cms。要從Sass生成CSS文件、最小化JavaScript文件,可以使用帶有圖形化界面的CodeKit或 Prepros 應(yīng)用。
  2. 將由應(yīng)用分隔的模板文件放到templates目錄中。如果模板文件代表一個頁面(如change_item.html 或 item_list.html),那么將其放到應(yīng)用的模板目錄中。如果模板中包含了另一個模板(如similar_items.html),將其放到includes子目錄中。同時模板目錄可對全局可復(fù)用腳本包含一個名為utils的目錄,如翻頁和語言選擇。

實現(xiàn)原理...

項目的整體文件結(jié)構(gòu)類似下面這樣:

myproject_website├── commands/├── db_backups/├── mockups/├── src/│ └── django-myproject/│ ├── externals/│ │ ├── apps/│ │ │ └── README.md│ │ └── libs/│ │ └── README.md│ ├── locale/│ ├── media/│ ├── myproject/│ │ ├── apps/│ │ │ ├── core/│ │ │ │ ├── __init__.py│ │ │ │ └── versioning.py│ │ │ └── __init__.py│ │ ├── settings/│ │ │ ├── __init__.py│ │ │ ├── _base.py│ │ │ ├── dev.py│ │ │ ├── production.py│ │ │ ├── sample_secrets.json│ │ │ ├── secrets.json│ │ │ ├── staging.py│ │ │ └── test.py│ │ ├── site_static/│ │ │ └── site/│ │ │ ├── css/│ │ │ │ └── style.css│ │ │ ├── img/│ │ │ │ ├── favicon-16x16.png│ │ │ │ ├── favicon-32x32.png│ │ │ │ └── favicon.ico│ │ │ ├── js/│ │ │ │ └── main.js│ │ │ └── scss/│ │ │ └── style.scss│ │ ├── templates/│ │ │ ├── base.html│ │ │ └── index.html│ │ ├── __init__.py│ │ ├── urls.py│ │ └── wsgi.py│ ├── requirements/│ │ ├── _base.txt│ │ ├── dev.txt│ │ ├── production.txt│ │ ├── staging.txt│ │ └── test.txt│ ├── static/│ ├── LICENSE│ └── manage.py*└── venv/

擴展知識...

要加速按剛剛所描述的方式創(chuàng)建項目,可以使用https://github.com/archatas/django-myproject框架模板。下載代碼后,執(zhí)行全局搜索并將myproject替換為對你有意義的名稱,這時就可以開始使用了。

其它內(nèi)容

通過pip處理項目依賴

安裝和管理Python包最便捷的工具是pip。不需要一個個地安裝每個包,可以將所要安裝的包列表定義到文本文件中。將文件傳遞給pip工具,然后它就會自動處理列表中所有包的安裝。這種方法的另一個好處是可以對包列表進行版本控制。

通常來說,有一個匹配生產(chǎn)環(huán)境的依賴文件就足夠了??梢栽陂_發(fā)機器上修改版本或添加、刪除依賴,然后通過版本控制來進行管理。這種方式通過一組依賴(及其關(guān)聯(lián)的修改)遷移到另一組依賴就和切換分支一樣簡單。

在某些情況下,環(huán)境差異太大,項目會需要有至少兩個實例。

可能會有針對其它開發(fā)者的開發(fā)環(huán)境,或是在生產(chǎn)環(huán)境中使用但生產(chǎn)環(huán)境用不到的特殊工具。也可能會有測試及預(yù)發(fā)布環(huán)境,用于在本地和對外網(wǎng)站設(shè)置環(huán)境中進行測試。

為保持良好的可維護性,應(yīng)當(dāng)能夠在開發(fā)、測試、預(yù)發(fā)布和生產(chǎn)環(huán)境中安裝所需要的Python模塊。有些模塊是共享的,另一些模塊僅在某些環(huán)境中使用。本節(jié)中,讀者將學(xué)習(xí)到如何對多環(huán)境組織項目依賴并通過pip進行管理。

準(zhǔn)備工作

在繼續(xù)本小節(jié)之前,需要準(zhǔn)備好Django項目、安裝了pip并啟動虛擬環(huán)境。更多操作細節(jié),參見使用虛擬環(huán)境一節(jié)。

如何實現(xiàn)...

執(zhí)行如下步驟來為虛擬環(huán)境中的Django項目準(zhǔn)備好pip依賴:

  1. 我們進入處于版本控制下的Django項目,創(chuàng)建requirements目錄并包含如下文本文件:
  1. 編輯_base.txt并逐行添加在各個環(huán)境中所共享的Python模塊:
# requirements/_base.txt Django~=3.0.4 djangorestframework -e git://github.com/omab/python-social-auth.git@6b1e301c79#egg=python-social-auth
  1. 如果特定環(huán)境的依賴和_base.txt中的相同,在該環(huán)境的依賴文件中添加一行包含_base.txt,如下例所示:
# requirements/production.txt -r _base.txt
  1. 如果環(huán)境中有特殊的依賴,在包含_base.txt之后添加這些依賴,如以下代碼所示:
# requirements/dev.txt -r _base.txt coverage django-debug-toolbar selenium
  1. 可以在虛擬環(huán)境中運行如下命令來安裝開發(fā)環(huán)境(或?qū)ζ渌h(huán)境使用相就的命令)所需的依賴,如下:
(env)$ pip install -r requirements/dev.txt

實現(xiàn)原理...

前面的pip install命令,不論指定虛擬環(huán)境還是在全局執(zhí)行,會通過requirements/_base.txt 和 requirements/dev.txt安裝所有的項目依賴。可以看到,我們可以指定Django框架所需模塊的版本,甚至是Git倉庫中指定的提交(commit)來進行安裝,示例中對python-social-auth就是這么做的。

在項目中有多條依賴時,好的做法是控制使用固定的一些Python模板發(fā)行版本。這樣就不用擔(dān)心在依賴升級時影響到項目的完整度,因為升級可能會導(dǎo)致沖突或向后兼容性的問題。在部署項目或移交項目給新開發(fā)者時這尤為重要。

如果已經(jīng)通過pip逐一手動進行了項目依賴的安裝,可以在虛擬環(huán)境中通過如下命令生成requirements/_base.txt文件:

(env)$ pip freeze > requirements/_base.txt

擴展知識...

要想保持簡化,可以對所有環(huán)境使用相同的依賴,可以通過定義為所有依賴只生成一個文件requirements.txt,如下所示:

(env)$ pip freeze > requirements.txt要在新的虛擬環(huán)境中安裝這些模塊,僅需使用如下命令:

(env)$ pip install -r requirements.txt如果需要通過其它版本控制系統(tǒng)或本地路徑安裝Python庫,可以通過官方文檔學(xué)習(xí)pip的更多用法。

另一種管理Python依賴的方法是越來越流行的Pipenv??梢酝ㄟ^https://github.com/pypa/pipenv進行了解。

其它內(nèi)容

為開發(fā)、測試、預(yù)發(fā)布和生產(chǎn)環(huán)境配置設(shè)置

前面也提到了,在開發(fā)環(huán)境中創(chuàng)建新功能、在測試環(huán)境中進行測試,然后將網(wǎng)站放到預(yù)發(fā)布服務(wù)器上讓人們試用新功能。隨后將網(wǎng)站部署到生產(chǎn)服務(wù)器上對公眾開放。每個環(huán)境都可能會有特有的設(shè)置,本小節(jié)我們將學(xué)習(xí)如何組織這些設(shè)置。

準(zhǔn)備工作

在Django項目中,我們將為每個環(huán)境創(chuàng)建設(shè)置文件:包括開發(fā)、測試、預(yù)發(fā)布還有生產(chǎn)。

如何實現(xiàn)...

按照如下步驟來配置項目設(shè)置:

  1. 在myproject目錄中,創(chuàng)建settings Python模塊,包含如下文件:
  1. 將在啟動新Django項目時自動創(chuàng)建的settings.py中的內(nèi)容拷貝到settings/_base.py中。然后刪除settings.py
  2. 修改settings/_base.py中的BASE_DIR來指向上一級。一開始像下面這樣:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))修改后長下面這樣:BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  1. 如果環(huán)境的設(shè)置與共享設(shè)置一致,那么只需要通過_base.py導(dǎo)入即可,如下所示:
# myproject/settings/production.py from ._base import *
  1. 在其它文件中對特定的環(huán)境應(yīng)用想要添加或重寫的設(shè)置,例如,開發(fā)環(huán)境中的設(shè)置應(yīng)放在dev.py中,如下面的代碼片斷所示:
# myproject/settings/dev.py from ._base import * EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
  1. 修改manage.py 和myproject/wsgi.py文件來默認使用其中一種環(huán)境的設(shè)置,所修改的代碼行如下:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
  1. 應(yīng)當(dāng)將該行內(nèi)容修改如下:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.production')

實現(xiàn)原理...

默認Django管理命令使用myproject/settings.py中的設(shè)置。通過本小節(jié)中所定義的方法,可以將所有環(huán)境的非敏感設(shè)置放到config目錄中進行版本控制。另一方面,settings.py文件本身會被版本控制所忽略,并且僅包含當(dāng)前開發(fā)、測試、預(yù)發(fā)布或生產(chǎn)環(huán)境所必需的設(shè)置。

??對于每種環(huán)境,推薦在PyCharm的設(shè)置、env/bin/activate腳本或.bash_profile中單獨設(shè)置DJANGO_SETTINGS_MODULE環(huán)境變量

其它內(nèi)容

在設(shè)置中定義相對路徑

Django要求你在設(shè)置中定義各種文件路徑,如媒體文件的根路徑、靜態(tài)文件的根路徑、模板路徑以及翻譯文件路徑。對項目的每個開發(fā)者,路徑可以不同,因為可在任何地方設(shè)置虛擬環(huán)境并且用戶可能會使用macOS、Linux或Windows。即使使用Docker容器來封裝項目,定義絕對路徑也會降低可維護性和可移植性。在各種用例中,都有動態(tài)定義這些路徑的方式,讓它們成為Django項目目錄的相對位置。

準(zhǔn)備工作

啟動Django項目并打開settings/_base.py。

如何實現(xiàn)...

相應(yīng)地修改路徑相關(guān)的設(shè)置,而不是硬編碼本地目錄,如下:

# settings/_base.pyimport osBASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))# ... TEMPLATES = [{# ... DIRS: [ os.path.join(BASE_DIR, 'myproject', 'templates'), ],# ... }]# ... LOCALE_PATHS = [ os.path.join(BASE_DIR, 'locale'), ]# ... STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'myproject', 'site_static'), ]STATIC_ROOT = os.path.join(BASE_DIR, 'static') MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

實現(xiàn)原理...

默認Django設(shè)置包含一個BASE_DIR值,它是包含manage.py目錄的絕對路徑(通常比settings.py高一級或比settings/_base.py高兩級)。然后我們使用os.path.join()函數(shù)來設(shè)置所有路徑相對BASE_DIR的位置。

根據(jù)我們在創(chuàng)建項目文件結(jié)構(gòu)一節(jié)中所確定的目錄布局,我們將為前面的一些示例插入myproject來作為中間路徑塊,因為相關(guān)的文件夾在其中進行的創(chuàng)建。

其它內(nèi)容

處理敏感信息設(shè)置

在配置Django項目時,肯定會處理一些敏感信息,比如密碼和API密鑰。不推薦將這些信息放在版本控制中。有兩種存儲這類信息的方式:放在環(huán)境變量中或單獨不進行追蹤的文件中。本小節(jié)中我們就來探討這兩種方式。

準(zhǔn)備工作

項目的大部分設(shè)置都在所有環(huán)境中共享并存儲在版本控制中。這些可以直接放在設(shè)置文件中進行定義;但有一些針對項目實例特定環(huán)境或是需要安全強化的敏感信息,如數(shù)據(jù)庫或email。下面我們來探討使用環(huán)境變量來暴露這些信息。

如何實現(xiàn)...

要從環(huán)境變量中讀取敏感信息,可以執(zhí)行如下步驟:

  1. 在settings/_base.py的開頭,定義如下的get_secret() 函數(shù):
# settings/_base.py import os from django.core.exceptions import ImproperlyConfigured def get_secret(setting): """Get the secret variable or return explicit exception.""" try: return os.environ[setting] except KeyError: error_msg = f'Set the {setting} environment variable' # 譯者注:f-string 用法在3.6版本開始引入 raise ImproperlyConfigured(error_msg)
  1. 然后,在需要定義敏感值時,使用 get_secret() 函數(shù),如下例所示:
SECRET_KEY = get_secret('DJANGO_SECRET_KEY') DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': get_secret('DATABASE_NAME'), 'USER': get_secret('DATABASE_USER'), 'PASSWORD': get_secret('DATABASE_PASSWORD'), 'HOST': 'db', 'PORT': '5432', } }

實現(xiàn)原理...

如果在運行Django管理命令時使用了未定義的環(huán)境變量,會看到拋出的報錯信息,如 Set the DJANGO_SECRET_KEY environment variable(請設(shè)置DJANGO_SECRET_KEY環(huán)境變量)。

可以在PyCharm的配置、遠程服務(wù)器配置控制臺、env/bin/activate腳本、.bash_profile或直接像下面這樣在Terminal中設(shè)置環(huán)境變量:

$ export DJANGO_SECRET_KEY="此處修改為50個字符的隨機長字符串"$ export DATABASE_NAME="myproject"$ export DATABASE_USER="myproject"$ export DATABASE_PASSWORD="修改為數(shù)據(jù)庫密碼"注意對于所有密碼、API密鑰及其它需要在Django配置中使用的敏感信息都應(yīng)使用get_secret() 函數(shù)。

擴展知識...

除環(huán)境變量外,我們還可以使用版本控制以外的文本文件來存儲敏感信息。文件格式可以是YAML、INI、CSV或JSON,存放在硬盤的其它位置。例如,對于JSON文件,應(yīng)當(dāng)有一個像下面這樣的 get_secret()函數(shù):

# settings/_base.pyimport osimport jsonwith open(os.path.join(os.path.dirname(__file__), 'secrets.json'), 'r') as f: secrets = json.loads(f.read())def get_secret(setting): """Get the secret variable or return explicit exception.""" try: return secrets[setting] except KeyError: error_msg = f'Set the {setting} secret variable' raise ImproperlyConfigured(error_msg)它會讀取配置目錄中的secrets.json 文件,該文件至少應(yīng)當(dāng)有如下結(jié)構(gòu):

{ "DATABASE_NAME": "myproject", "DATABASE_USER": "myproject", "DATABASE_PASSWORD": "修改為數(shù)據(jù)庫密碼", "DJANGO_SECRET_KEY": "此處修改為50個字符的隨機長字符串" }確保在版本控制中忽略secrets.json文件,但為方便起見,可以創(chuàng)建一個帶有空值的sample_secrets.json 文件并對其進行版本控制:

{ "DATABASE_NAME": "", "DATABASE_USER": "", "DATABASE_PASSWORD": "", "DJANGO_SECRET_KEY": "此處修改為50個字符的隨機長字符串" }

其它內(nèi)容

在項目中包含外部依賴

有時,我們無法使用pip來安裝外部依賴,就需要將其直接放到項目中,如下面的用例:

在項目中包含外部依賴可以確保不論何時開發(fā)者對依賴進行升級,所有其他開發(fā)者都會在版本控制系統(tǒng)的下一次更新中收到升級后的版本。

準(zhǔn)備工作

應(yīng)在虛擬環(huán)境下啟動Django項目。

如何實現(xiàn)...

對一個虛擬環(huán)境項目逐一執(zhí)行如下步驟:

  1. 如尚未創(chuàng)建,在Django項目目錄django-myproject下創(chuàng)建externals目錄
  2. 然后在該目錄下創(chuàng)建libs和apps這兩個目錄。libs目錄用于項目所需用到的Python模塊, 例如Boto、Requests、Twython和Whoosh。apps用于第三方Django應(yīng)用,如Django CMS、Django Haystack和django-storages。 我們強烈推薦在libs和apps目錄中創(chuàng)建README.md文件,在其中描述各個模塊的用途、版本或修訂版,以及其來源。
  3. 目錄結(jié)構(gòu)類似下面這樣:
externals/ ├── apps │ ├── README.md │ ├── cms │ ├── haystack │ └── storages └── libs ├── README.md ├── boto ├── requests └── twython
  1. 下一步是將外部庫和應(yīng)用放到Python路徑中,這樣就可以像已安裝過那樣進行識別了??赏ㄟ^在配置中添加如下代碼來實現(xiàn):
# settings/_base.py import os import sys BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) EXTERNAL_BASE = os.path.join(BASE_DIR, "externals") EXTERNAL_LIBS_PATH = os.path.join(EXTERNAL_BASE, "libs") EXTERNAL_APPS_PATH = os.path.join(EXTERNAL_BASE, "apps") sys.path = ["", EXTERNAL_LIBS_PATH, EXTERNAL_APPS_PATH] + sys.path

實現(xiàn)原理...

模塊應(yīng)當(dāng)放到Python路徑下來讓Python可以運行并導(dǎo)入該模塊。將模塊放到Python路徑中的一種方式是在導(dǎo)入非常規(guī)位置中的模塊前修改sys.path變量。像設(shè)置中指定的sys.path的值,是一個目錄列表,其中的空字符串表示當(dāng)前目錄,隨后是項目中的目錄,最后是Python軟件的全局共享目錄。可以在Python shell中查看sys.path的值,如下:

(env)$ python manage.py shell >>> import sys>>> sys.path在嘗試導(dǎo)入模塊時,Python在列表中搜索模塊并在查找到時返回每一個結(jié)果。

因此,我們首先定義了BASE_DIR變量,它是django-myproject的絕對路徑或是比myproject/settings/_base.py高3級。然后,我們定義了變量EXTERNAL_LIBS_PATH和EXTERNAL_APPS_PATH,它們是BASE_DIR的相對路徑。最后,我們修改了sys.path屬性,在列表的開頭添加新路徑。注意我們還將空字符串添加為第一個路徑用于搜索,表示在查找其它Python路徑之前應(yīng)總是先檢查當(dāng)前目錄中的模塊。

這種包含外部庫的方式對于有C語言約束的Python包不具備跨平臺性,比如lxml。對于這種依賴,我們推薦按照通過pip處理項目依賴一節(jié)中所介紹的方式來使用pip安裝依賴。

其它內(nèi)容

動態(tài)設(shè)置STATIC_URL

如果將STATIC_URL設(shè)置為靜態(tài)值,那么在每次更新CSS文件、JavaScript文件或圖片時,你和網(wǎng)站用戶會需要清除瀏覽器緩存來讓修改生效。有一個技巧可以避免清除瀏覽器緩存 ,那就是對STATIC_UR中L所顯示的最新修改添加時間戳。不論何時修改代碼,訪客的瀏覽器會強制加載所有新的靜態(tài)文件。

本小節(jié)中,我們將學(xué)習(xí)如何為Git用戶在STATIC_URL中添加時間戳。

準(zhǔn)備工作

確保項目處于Git的版本控制中并且已經(jīng)在設(shè)置中定義了BASE_DIR,在設(shè)置中定義相對路徑一節(jié)中進行過介紹。

如何實現(xiàn)...

將Git時間戳放到STATIC_URL設(shè)置中包含下面兩個步驟:

  1. 如尚未創(chuàng)建,請在Django項目中創(chuàng)建myproject.apps.core應(yīng)用。應(yīng)在里面創(chuàng)建一個versioning.py文件:
# versioning.py import subprocess from datetime import datetime def get_git_changeset_timestamp(absolute_path): repo_dir = absolute_path git_log = subprocess.Popen( "git log --pretty=format:%ct --quiet -1 HEAD", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=repo_dir, universal_newlines=True, ) timestamp = git_log.communicate()[0] try: timestamp = datetime.utcfromtimestamp(int(timestamp)) except ValueError: # Fallback to current timestamp return datetime.now().strftime('%Y%m%d%H%M%S') changeset_timestamp = timestamp.strftime('%Y%m%d%H%M%S') return changeset_timestamp
  1. 在設(shè)置中導(dǎo)入新創(chuàng)建的get_git_changeset_timestamp()函數(shù)并對STATIC_URL進行使用,如下:
# settings/_base.py from myproject.apps.core.versioning import get_git_changeset_timestamp # ... timestamp = get_git_changeset_timestamp(BASE_DIR) STATIC_URL = f'/static/{timestamp}/'

實現(xiàn)原理...

get_git_changeset_timestamp()接收absolute_path目錄作為參數(shù)并通過該參數(shù)調(diào)用git log shell命令來顯示目錄中的HEAD修訂版的Unix時間戳。我們將BASE_DIR傳遞給該函數(shù),因為要確保它處于版本控制中。在解析時間戳?xí)r,轉(zhuǎn)化為一個由所返回年、月、日、小時、分鐘和秒組成的字符串,然后在STATIC_URL的定義中包含它。

擴展知識...

這種方法僅在每種環(huán)境包含項目的完整Git倉庫,在某些情況下,比如使用Heroku或Docker來進行部署時,無法訪問Git倉庫和遠程服務(wù)器中的git log 命令。這時要為STATIC_URL添加動態(tài)區(qū)塊,則需要從文件文件中讀取時間戳,例如每次提交時都會更新的myproject/settings/last-modified.txt。

這時,設(shè)置將包含如何內(nèi)容:

# settings/_base.pywith open(os.path.join(BASE_DIR, 'myproject', 'settings', 'last-modified.txt'), 'r') as f: timestamp = f.readline().strip() STATIC_URL = f'/static/{timestamp}/'可以通過預(yù)提交鉤子(hook)來讓Git倉庫更新last-modified.txt。這一可執(zhí)行bash腳本名稱就為pre-commit并放在django-myproject/.git/hooks/目錄下:

# django-myproject/.git/hooks/pre-commit#!/usr/bin/env pythonfrom subprocess import check_output, CalledProcessErrorimport osfrom datetime import datetimedef root(): ''' returns the absolute path of the repository root ''' try: base = check_output(['git', 'rev-parse', '--show-toplevel']) except CalledProcessError: raise IOError('Current working directory is not a git repository') return base.decode('utf-8').strip()def abspath(relpath): '''returns the absolute path for a path given relative to the root of the git repository''' return os.path.join(root(), relpath)def add_to_git(file_path): ''' adds a file to git ''' try: base = check_output(['git', 'add', file_path]) except CalledProcessError: raise IOError('Current working directory is not a git repository') return base.decode('utf-8').strip()def main(): file_path = abspath("myproject/settings/last-modified.txt") with open(file_path, 'w') as f: f.write(datetime.now().strftime("%Y%m%d%H%M%S")) add_to_git(file_path)if __name__ == '__main__': main()這個腳本會提交到Git倉庫時更新last-modified.txt并將該文件添加到Git索引中。

其它內(nèi)容

為MySQL配置設(shè)置UTF-8為默認編碼

MySQL自稱是最為流行的開源數(shù)據(jù)庫。在本小節(jié)中,我們將講解如何將其默認編碼設(shè)置為UTF-8。注意如果你沒有在數(shù)據(jù)庫配置中設(shè)置編碼,很可能在應(yīng)用UTF-8編輯的數(shù)據(jù)使用的是LATIN1。這會在使用€這樣的符號是出現(xiàn)數(shù)據(jù)庫錯誤。本小節(jié)還省卻你將LATIN1轉(zhuǎn)化為UTF-8麻煩,尤其是在對一些表使用LATIN1而另一些表使用UTF-8編碼時。

準(zhǔn)備工作

確保安裝了MySQL數(shù)據(jù)庫管理系統(tǒng)及mysqlclient Python模塊,并且在項目的設(shè)置中使用MySQL引擎。

如何實現(xiàn)...

使用你常用的編輯器打開MySQL配置文件/etc/mysql/my.cnf,確保下面的設(shè)置分別在[client]、[mysql]和[mysqld]版塊中 ,如下:

# /etc/mysql/my.cnf[client] default-character-set = utf8[mysql] default-character-set = utf8[mysqld]collation-server = utf8_unicode_ci init-connect = 'SET NAMES utf8' character-set-server = utf8如果以上有任何版塊不存在,請自行在文件中添加。如果各版塊已經(jīng)存在,將設(shè)置添加到已有的配置中,并使用命令行工具重啟MySQL,如下:

$ /etc/init.d/mysql restart

實現(xiàn)原理...

此時再新建MySQL數(shù)據(jù)時,所有的數(shù)據(jù)庫和數(shù)據(jù)表默認都設(shè)置為UTF-8編碼。別忘記在開發(fā)或發(fā)布項目的所有電腦上進行這一設(shè)置。

擴展知識...

在PostgreSQL中,默認服務(wù)器編碼已經(jīng)是UTF-8是,但如果你想要顯式地使用UTF-8編碼創(chuàng)建PostgreSQL數(shù)據(jù)庫,那么可以使用如下命令來實現(xiàn):

$ createdb --encoding=UTF8 --locale=en_US.UTF-8 --template=template0 myproject

其它內(nèi)容

創(chuàng)建 Git ignore文件

Git是最流行的分布式版本控制系統(tǒng),你可能已經(jīng)在Django項目中使用它了。雖然是對絕大多數(shù)文件追蹤修改,但推薦將一些特定的文件及文件夾放在版本控制以外。通常緩存、已編譯代碼、日志文件及隱藏的系統(tǒng)文件都不應(yīng)在Git倉庫中跟蹤記錄。

準(zhǔn)備工作

確保你的Django項目處于Git版本控制之中。

如何實現(xiàn)...

使用你喜歡的編輯器在Django項目的根目錄下創(chuàng)建一個.gitignore文件,并在其中加入如下的文件和目錄:

# .gitignore### Python template# Byte-compiled / optimized / DLL files __pycache__/*.py[cod]*$py.class# Installer logspip-log.txt pip-delete-this-directory.txt# Unit test / coverage reports htmlcov/.tox/.nox/.coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/# Translations *.mo*.pot# Django stuff: *.log db.sqlite3# Sphinx documentationdocs/_build/# IPython profile_default/ ipython_config.py# Environmentsenv/# Media and Static directories /media/!/media/.gitkeep/static/ !/static/.gitkeep# Secrets secrets.json

實現(xiàn)原理...

.gitignore指定不由Git版本控制系統(tǒng)所追蹤的模式。本小節(jié)中所創(chuàng)建的.gitignore文件會忽略掉Python編譯文件、本地設(shè)置、采集的靜態(tài)文件以及帶有上傳文件的媒體目錄。

注意對媒體和靜態(tài)文件有一些感嘆號標(biāo)記的例外語法:

/media/ !/media/.gitkeep這告訴Git要忽略 /media/目錄但保持使用版本控制追蹤 /media/.gitkeep文件。因為Git版本控制僅追蹤文件而不是目錄,所以我們使用.gitkeep 來確保在每個環(huán)境中都會創(chuàng)建media目錄,但不進行追蹤。

其它內(nèi)容

刪除Python編譯文件

首次運行項目時,Python使用字節(jié)碼編譯文件.pyc來對所有的.py 代碼進行編譯,并在稍后執(zhí)行時使用。通常在修改.py文件時,.pyc會重新進行編譯;但有時在切換分支或移動目錄時,需要手動清理掉這些編譯文件。

準(zhǔn)備工作

使用你喜歡的編輯器在家目錄中編輯或創(chuàng)建一個.bash_profile文件。

如何實現(xiàn)...

  1. 在.bash_profile文件的最后添加這一別名,如下:
```

~/.bash_profile

alias delpyc='find . -name "*.py[co]" -deletefind . -type d -name "__pycache__" -delete'2. 此時要清理Python編譯后文件,進入項目目錄并在命令行中輸入如下命令: ```(env)$ delpyc

實現(xiàn)原理...

首先我們創(chuàng)建了一個Unix別名來搜索.pyc 和 .pyo文件以及pycache目錄并在當(dāng)前目錄及其子目錄中刪除它們。在命令行工具中啟動新會話時會執(zhí)行.bash_profile文件。

擴展知識...

如果想要避免一起創(chuàng)建Python編譯后文件,可以在.bash_profile、env/bin/activate 腳本或 PyCharm配置中設(shè)置一個環(huán)境變量PYTHONDONTWRITEBYTECODE=1。

其它內(nèi)容

在Python文件中重視導(dǎo)入順序

在創(chuàng)建Python模塊時,一個好的實踐是對文件結(jié)構(gòu)保持連貫性。這會讓你自己和其他開發(fā)人員在閱讀代碼時更為容易。本節(jié)展示如何架構(gòu)導(dǎo)入文件。

準(zhǔn)備工作

創(chuàng)建一個虛擬環(huán)境并在其中創(chuàng)建一個Django項目。

如何實現(xiàn)...

為你所創(chuàng)建的Python文件應(yīng)用如下的結(jié)構(gòu)。將導(dǎo)入分類成不同的版塊,如下:

# 系統(tǒng)庫 import os import re from datetime import datetime# 第三方庫import botofrom PIL import Image# Django模塊from django.db import models from django.conf import settingsfrom cms.models import Page# 當(dāng)前應(yīng)用模塊from .models import NewsArticle from . import app_settings

實現(xiàn)原理...

對于導(dǎo)入我們有5個主要分類,如下:

擴展知識...

在Python和Django中進行編碼時,使用Python代碼的官方樣式指南PEP 8??梢栽?span >https://www.python.org/dev/peps/pep-0008/中進行查看。

其它內(nèi)容

創(chuàng)建應(yīng)用配置

Django項目由多個稱為應(yīng)用(或更普遍地稱為app)的Python模塊所組成,它將不同的模塊化功能進行拼接。每個應(yīng)用可包含模型、視圖、表單、URL配置、管理命令、數(shù)據(jù)庫遷移、信號、測試、上下文處理器和middleware等等。Django框架有一個應(yīng)用倉庫,所有的應(yīng)用和模型在這里匯集用于稍后的配置和審查。從Django 1.7開始在每個應(yīng)用的AppConfig實例中保存有應(yīng)用的元信息。我們來創(chuàng)建一個magazine示例應(yīng)用,學(xué)習(xí)如何使用應(yīng)用配置。

準(zhǔn)備工作

可以通過調(diào)用startapp管理命令來創(chuàng)建Django應(yīng)用或手動創(chuàng)建應(yīng)用模塊:

(env)$ cd myproject/apps/(env)$ django-admin.py startapp magazine創(chuàng)建好magazine應(yīng)用之后,在models.py中添加一個NewsArticle模型、在admin.py中為該模型配置管理界面并在設(shè)置的INSTALLED_APPS中添加myproject.apps.magazine。如何對于這些操作你還不熟悉,請參照Django官方課程進行學(xué)習(xí)。

如何實現(xiàn)...

按照如下步驟來創(chuàng)建和使用應(yīng)用配置:

  1. 修改apps.py文件并在其中插入如下內(nèi)容:
# myproject/apps/magazine/apps.py from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ class MagazineAppConfig(AppConfig): name = "myproject.apps.magazine" verbose_name = _("Magazine") def ready(self): from . import signals
  1. 在magazine模塊中編輯init.py來包含如下內(nèi)容
# myproject/apps/magazine/__init__.py default_app_config = "myproject.apps.magazine.apps.MagazineAppConfig"
  1. 我們來創(chuàng)建一個signals.py文件并在其中添加一些信號處理器:
# myproject/apps/magazine/signals.py from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.conf import settings from .models import NewsArticle @receiver(post_save, sender=NewsArticle) def news_save_handler(sender, **kwargs): if settings.DEBUG: print(f"{kwargs['instance']} saved.") @receiver(post_delete, sender=NewsArticle) def news_delete_handler(sender, **kwargs): if settings.DEBUG: print(f"{kwargs['instance']} deleted.")

實現(xiàn)原理...

在運行HTTP服務(wù)或調(diào)用管理命令時,會調(diào)用django.setup()。它會加載設(shè)置、配置日志并準(zhǔn)備應(yīng)用倉庫。這一倉庫通過3個步驟進行初始化。首先從設(shè)置的INSTALLED_APPS中導(dǎo)入每項應(yīng)用的配置。這些項可指向應(yīng)用名或直接指向配置,如"myproject.apps.magazine" 或 "myproject.apps.magazine.apps.MagazineAppConfig"。

然后Django嘗試通過INSTALLED_APPS中的每個應(yīng)用導(dǎo)入models.py并匯集所有模型。

最后,Django對每個應(yīng)用配置運行ready()方法。該方法是開發(fā)過程中注冊信號處理器(若有)一個很好的點。ready() 方法是可選的。

在我們的示例中,MagazineAppConfig類設(shè)置magazine應(yīng)用的配置。name參數(shù)定義了當(dāng)前應(yīng)用的模塊。verbose_name參數(shù)定義了Django模型管理后臺中供人閱讀的名稱,在這一后臺中模型按照應(yīng)用進行分組。ready()方法導(dǎo)入并激活信號處理器,在DEBUG模型下,向終端打印NewsArticle被保存或刪除。

擴展知識...

在調(diào)用django.setup()之后,可以像下面這樣通過倉庫加載應(yīng)用配置及模型:

>>> from django.apps import apps as django_apps>>> magazine_app_config = django_apps.get_app_config("magazine") >>> magazine_app_config<MagazineAppConfig: magazine>>>> magazine_app_config.models_module<module 'magazine.models' from '/path/to/myproject/apps/magazine/models.py'>>>> NewsArticle = django_apps.get_model("magazine", "NewsArticle") >>> NewsArticle<class 'magazine.models.NewsArticle'>譯者注:以上請在django-myproject目錄下操作并設(shè)置環(huán)境變量export DJANGO_SETTINGS_MODULE='myproject.settings.xxx'

可以在Django官方文檔中閱讀更多有關(guān)應(yīng)用配置的信息。

其它內(nèi)容

定義可重寫的app設(shè)置

本小節(jié)將展示如何定義應(yīng)用的設(shè)置并可在項目設(shè)置文件中進行重寫。這對于可復(fù)用應(yīng)用猶為有用,可以通過添加配置來實現(xiàn)自定義。

準(zhǔn)備工作

按照創(chuàng)建應(yīng)用配置一節(jié)中準(zhǔn)備工作的步驟創(chuàng)建Django應(yīng)用。

如何實現(xiàn)...

  1. 如果只有一兩個設(shè)置在models.py中使用getattr()模式來定義應(yīng)用設(shè)置,或者設(shè)置很多并希望更好地進行組織的話在app_settings.py文件中進行定義 :
# myproject/apps/magazine/app_settings.py from django.conf import settings from django.utils.translation import gettext_lazy as _ # Example: SETTING_1 = getattr(settings, "MAGAZINE_SETTING_1", "default value") MEANING_OF_LIFE = getattr(settings, "MAGAZINE_MEANING_OF_LIFE", 42) ARTICLE_THEME_CHOICES = getattr( settings, "MAGAZINE_ARTICLE_THEME_CHOICES", [ ('futurism', _("Futurism")), ('nostalgia', _("Nostalgia")), ('sustainability', _("Sustainability")), ('wonder', _("Wonder")), ] )
  1. models.py中包含NewsArticle模型,如下:
```

myproject/apps/magazine/models.py

from django.db import modelsfrom django.utils.translation import gettext_lazy as _class NewsArticle(models.Model): created_at = models.DateTimeField(_("Created at"), auto_now_add=True) title = models.CharField(_("Title"), max_length=255) body = models.TextField(_("Body")) theme = models.CharField(_("Theme"), max_length=20) class Meta: verbose_name = _("News Article") verbose_name_plural = _("News Articles") def __str__(self): return self.title3. 然后在admin.py中我們將導(dǎo)入并使用app_settings.py中的設(shè)置,如下:

myproject/apps/magazine/admin.py

from django import formsfrom django.contrib import adminfrom .models import NewsArticlefrom .app_settings import ARTICLE_THEME_CHOICESclass NewsArticleModelForm(forms.ModelForm): theme = forms.ChoiceField( label=NewsArticle._meta.get_field("theme").verbose_name, choices=ARTICLE_THEME_CHOICES, required=not NewsArticle._meta.get_field("theme").blank, ) class Meta: fields = "__all__"@admin.register(NewsArticle)class NewsArticleAdmin(admin.ModelAdmin): form = NewsArticleModelForm4. 如果希望重寫給定項目中的ARTICLE_THEME_CHOICES設(shè)置,應(yīng)在項目設(shè)置中添加MAGAZINE_ARTICLE_THEME_CHOICES:

myproject/settings/_base.py

from django.utils.translation import gettext_lazy as _ # ...MAGAZINE_ARTICLE_THEME_CHOICES = [ ('futurism', _("Futurism")), ('nostalgia', _("Nostalgia")), ('sustainability', _("Sustainability")), ('wonder', _("Wonder")), ('positivity', _("Positivity")), ('solutions', _("Solutions")), ('science', _("Science")),]### 實現(xiàn)原理...getattr(object, attribute_name[, default_value]) Python函數(shù)嘗試通過object獲取attribute_name屬性并在未查找到時返回default_value。我們會從Django設(shè)置塊中讀取不同的設(shè)置,在不存在時使用默認值。注意我們也可以在models.py中定義theme字段的選項,但這里在后臺管理代碼中創(chuàng)建了一個自定義的ModelForm并其中設(shè)置了選項。這樣做是避免在每次修改ARTICLE_THEME_CHOICES的時候產(chǎn)生一次新的數(shù)據(jù)庫遷移。### 其它內(nèi)容* *創(chuàng)建應(yīng)用配置*一節(jié)* **第6章 模型管理**## 使用Docker容器處理Django, Gunicorn, Nginx和PostgreSQLDjango項目不僅有Python包的依賴,還對系統(tǒng)有很多要求,如web服務(wù)器、數(shù)據(jù)庫、服務(wù)端緩存和郵件服務(wù)。在開發(fā)Django項目時,需要確保所有環(huán)境和所有開發(fā)者安裝了相同的依賴。一種保持這些依賴同步的方式是使用Docker。通過Docker,可以對每個項目單獨擁有不同版本的數(shù)據(jù)庫、web 或其它服務(wù)。Docker是一種用于創(chuàng)建稱之為容器的帶配置、自定義虛擬機的系統(tǒng)。它允許我們精確地復(fù)制生產(chǎn)環(huán)境的設(shè)置。Docker通過稱為Docker鏡像的東西進行創(chuàng)建。鏡像由如何構(gòu)建容器的層(或指令)組成??梢杂蠵ostgreSQL鏡像、Redis鏡像、Memcached鏡像,以及針對Django項目的自定義鏡像,所有這些鏡像可通過Docker Compose 整合成關(guān)聯(lián)的容器。在本小節(jié)中,我們將使用項目樣板來設(shè)置帶有數(shù)據(jù)庫的Django項目,由Nginx和Gunicorn提供服務(wù)、并通過Docker Compose對它們進行管理。### 準(zhǔn)備工作首先,我們需要安裝Docker引擎,可按照https://www.docker.com/get-started上的教程進行安裝。這通常包含Compose工具,讓我們可以管理需要多個容器的系統(tǒng),對于隔離環(huán)境的Django也非常理想。如果要單獨安裝,可參見https://docs.docker.com/compose/install/中有關(guān)Compose的內(nèi)容。**譯者注:** 考慮到國內(nèi)的網(wǎng)絡(luò)環(huán)境可配置 Docker 倉庫的加速:Preferences>Docker Engine,如添加"registry-mirrors": [ "http://hub-mirror.c.163.com", ]

### 如何實現(xiàn)...我們來共同探討下Django和Docker樣板:1. 將https://github.com/archatas/django_docker中的代碼下載到本地,如~/projects/django_docker 目錄中。 > ??如果你選擇的是其它目錄,如myproject_docker,那么需要進行全局搜索并將django_docker替換為myproject_docker。2. 打開docker-compose.yml文件。需要創(chuàng)建3個容器:nginx, gunicorn和db。不必擔(dān)心它看上去很復(fù)雜,我們將在后面詳細講解:

docker-compose.yml

version: "3.7"services: nginx: image: nginx:latest ports: - "80:80" volumes: - ./config/nginx/conf.d:/etc/nginx/conf.d - static_volume:/home/myproject/static - media_volume:/home/myproject/media depends_on: - gunicorn gunicorn: build: context: . args: PIP_REQUIREMENTS: "${PIP_REQUIREMENTS}" command: bash -c "/home/myproject/env/bin/gunicorn --workers 3 --bind 0.0.0.0:8000 myproject.wsgi:application" depends_on: - db volumes: - static_volume:/home/myproject/static - media_volume:/home/myproject/media expose: - "8000" environment: DJANGO_SETTINGS_MODULE: "${DJANGO_SETTINGS_MODULE}" DJANGO_SECRET_KEY: "${DJANGO_SECRET_KEY}" DATABASE_NAME: "${DATABASE_NAME}" DATABASE_USER: "${DATABASE_USER}" DATABASE_PASSWORD: "${DATABASE_PASSWORD}" EMAIL_HOST: "${EMAIL_HOST}" EMAIL_PORT: "${EMAIL_PORT}" EMAIL_HOST_USER: "${EMAIL_HOST_USER}" EMAIL_HOST_PASSWORD: "${EMAIL_HOST_PASSWORD}" db: image: postgres:latest restart: always environment: POSTGRES_DB: "${DATABASE_NAME}" POSTGRES_USER: "${DATABASE_USER}" POSTGRES_PASSWORD: "${DATABASE_PASSWORD}" ports: - 5432 volumes: - postgres_data:/var/lib/postgresql/data/volumes: postgres_data: static_volume: media_volume:3. 打開并通讀Dockerfile文件。有創(chuàng)建gunicorn容器所需要的一些層(或指令):

Dockerfile

# pull official base image FROM python:3.8# accept argumentsARG PIP_REQUIREMENTS=production.txt# set environment variablesENV PYTHONDONTWRITEBYTECODE 1ENV PYTHONUNBUFFERED 1# install dependenciesRUN pip install --upgrade pip setuptools# create user for the Django project RUN useradd -ms /bin/bash myproject# set current userUSER myproject# set work directoryWORKDIR /home/myproject# 添加以下內(nèi)容避免因 Volume 所帶來的 root 權(quán)限問題RUN mkdir static && mkdir media# create and activate virtual environment RUN python3 -m venv env# copy and install pip requirementsCOPY --chown=myproject ./src/myproject/requirements /home/myproject/requirements/# 添加豆瓣源在國內(nèi)加速RUN ./env/bin/pip3 install -i https://pypi.doubanio.com/simple/ -r /home/myproject/requirements/${PIP_REQUIREMENTS}# copy Django project filesCOPY --chown=myproject ./src/myproject /home/myproject/4. 復(fù)制build_dev_example.sh 腳本到build_dev.sh中并編輯其內(nèi)容。有一些要傳遞給docker-compose腳本的環(huán)境變量:

build_dev.sh

#!/usr/bin/env bash DJANGO_SETTINGS_MODULE=myproject.settings.dev /DJANGO_SECRET_KEY="change-this-to-50-characters-long-random-string" /DATABASE_NAME=myproject /DATABASE_USER=myproject /DATABASE_PASSWORD="change-this-too" /PIP_REQUIREMENTS=dev.txt /docker-compose up --detach --build5. 在命令行工具中,為build_dev.sh添加執(zhí)行權(quán)限并運行它來構(gòu)建容器:$ chmod +x build_dev.sh $ ./build_dev.sh

6. 如果此時訪問http://0.0.0.0/en/,應(yīng)當(dāng)會看到Hello, World!頁面。 在訪問http://0.0.0.0/en/admin/時,會看到如下內(nèi)容:OperationalError at /en/admin/ FATAL: role "myproject" does not exist

這表示你需要在Docker容器中創(chuàng)建數(shù)據(jù)庫用戶及數(shù)據(jù)庫。7. 我們通過SSH連接db容器并在Docker容器中創(chuàng)建數(shù)據(jù)庫用戶、密碼及數(shù)據(jù)庫本身:$ docker exec -it django_docker_db_1 bash /# su - postgres /$ createuser --createdb --password myproject /$ createdb --username myproject myproject

在被詢問時,輸入與build_dev.sh腳本中相同的數(shù)據(jù)庫密碼。(如未出現(xiàn)6中的報錯則無需手動執(zhí)行以上用戶創(chuàng)建) 連續(xù)按兩次Ctrl + D來登出PostgreSQL用戶和Docker容器?,F(xiàn)在訪問http://0.0.0.0/en/admin/的話,會看到如下內(nèi)容: ```ProgrammingError at /en/admin/ relation "django_session" does not exist LINE 1: ...ession_data", "django_session"."expire_date" FROM "django_se...這表示我們需要運行遷移來創(chuàng)建數(shù)據(jù)庫模式。
  1. 通過SSH進入gunicorn容器并運行必要的Django管理命令:
$ docker exec -it django_docker_gunicorn_1 bash $ source env/bin/activate (env)$ python manage.py migrate (env)$ python manage.py collectstatic (env)$ python manage.py createsuperuser回復(fù)管理命令所詢問的所有問題。按兩次Ctrl + D登出Docker容器。如果現(xiàn)在導(dǎo)航至http:/?/?0.?0.?0.?0/?en/?admin/?,應(yīng)當(dāng)會看到Django后臺管理頁面,在這里可以通過所創(chuàng)建的超級用戶來進行登錄。
  1. 創(chuàng)建相似的腳本 build_test.sh、build_staging.sh和build_production.sh,其中僅環(huán)境變量存在差別。

實現(xiàn)原理...

樣板中的代碼結(jié)構(gòu)類似于虛擬環(huán)境中的那個。項目源文件放在src目錄中。這里有作為預(yù)提交鉤子的git-hooks目錄用于追蹤最后一次變更日期及容器中使用的服務(wù)配置目錄:

django_docker/├── Dockerfile├── LICENSE├── README.md├── build_dev_example.sh├── config│ └── nginx│ └── conf.d│ └── myproject.conf├── docker-compose.yml├── git-hooks│ ├── install_hooks.sh│ └── pre-commit└── src └── myproject ├── locale │ └── de │ └── LC_MESSAGES │ └── django.po ├── manage.py ├── myproject │ ├── __init__.py │ ├── apps │ │ └── __init__.py │ ├── settings │ │ ├── __init__.py │ │ ├── _base.py │ │ ├── dev.py │ │ ├── last-update.txt │ │ ├── production.py │ │ ├── staging.py │ │ └── test.py │ ├── site_static │ │ └── site │ │ ├── css │ │ │ └── style.css │ │ ├── img │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ └── favicon.ico │ │ ├── js │ │ │ └── main.js │ │ └── scss │ │ └── style.scss │ ├── templates │ │ ├── base.html │ │ └── index.html │ ├── urls.py │ └── wsgi.py └── requirements ├── _base.txt ├── dev.txt ├── production.txt ├── staging.txt └── test.txtDocker相關(guān)的主要配置在docker-compose.yml和Dockerfile中。Docker Compose是對Docker的命令行API進行的封裝。build_dev.sh腳本運行Django項目,Gunicorn WSGI HTTP服務(wù)器的端口號為8000,Nginx的端口號為80(對靜態(tài)和媒體文件提供服務(wù)并將請求代理到Gunicorn),PostgreSQL數(shù)據(jù)庫的端口為5432。

在docker-compose.yml文件中,要求創(chuàng)建三個Docker容器:

nginx和db通過位于https://hub.docker.com/的官方鏡像進行創(chuàng)建。它們有具體的配置參數(shù),如所運行的端口、環(huán)境變量、對其它容器的依賴和磁盤卷(Volume)。

Docker磁盤卷是在重建Docker 容器不會被改變的特殊目錄。需要定義磁盤卷來供數(shù)據(jù)庫數(shù)據(jù)文件、媒體、靜態(tài)文件等使用。

gunicorn容器通過Dockerfile中的指令進行構(gòu)建,由docker-compose.yml文件中的構(gòu)建上下文進行定義。我們來詳解里面的每一層(或命令):

config/nginx/conf.d/myproject.conf中的內(nèi)容會保存到nginx容器的/etc/nginx/conf.d/中。Nginx web服務(wù)器的這一配置是說要監(jiān)聽80端口(默認HTTP端口)并轉(zhuǎn)發(fā)端口為8000的Gunicorn服務(wù)的請求,要求靜態(tài)或媒體內(nèi)容的請求除外:

#/etc/nginx/conf.d/myproject.confupstream myproject { server django_docker_gunicorn_1:8000;}server { listen 80; location / { proxy_pass http://myproject; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_redirect off; } rewrite "/static//d+/(.*)" /static/$1 last; location /static/ { alias /home/myproject/static/; } location /media/ { alias /home/myproject/media/; } }第12章 部署中的在預(yù)發(fā)布環(huán)境中基于Nginx和Gunicorn部署在生產(chǎn)環(huán)境中基于Nginx和Gunicorn部署小節(jié)中會學(xué)習(xí)更多有關(guān)Nginx和Gunicorn配置的知識。

擴展知識...

可以通過docker-compose down命令銷毀Docker容器及使用構(gòu)建腳本重建這些容器:

$ docker-compose down $ ./build_dev.sh如果和所預(yù)期的效果不一致,可以通過docker-compose logs命令來檢查日志:

$ docker-compose logs nginx$ docker-compose logs gunicorn $ docker-compose logs db通過SSH來連接其中的任一容器,可以使用下面的命令:

$ docker exec -it django_docker_gunicorn_1 bash $ docker exec -it django_docker_nginx_1 bash$ docker exec -it django_docker_db_1 bash可以使用docker cp命令將Docker容器的數(shù)據(jù)卷進行拷入和拷出文件、目錄的處理:

$ docker cp ~/avatar.png django_docker_gunicorn_1:/home/myproject/media/ $ docker cp django_docker_gunicorn_1:/home/myproject/media ~/Desktop/如果希望更好地掌握Docker和Docker Compose,可以學(xué)習(xí)官方文檔的內(nèi)容,尤其是https://docs.docker.com/compose/部分。

其它內(nèi)容

本文首發(fā)于Alan Hou的個人博客

關(guān)鍵詞:入門,發(fā)指

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關(guān)閉