在這個比較 Python 框架的最後一篇中,讓我們看看 Django。
在本系列(由四部分組成)的前三篇文章中,我們討論了 Pyramid、Flask 和 Tornado 這 3 個 Web 框架。我們已經構建了三次相同的應用程式,最終我們遇到了 Django。總的來說,Django 是目前 Python 開發人員使用的主要 Web 框架,並且原因顯而易見。它擅長隱藏大量的設定邏輯,讓你專注於能夠快速構建大型應用程式。
也就是說,當涉及到小型專案時,比如我們的待辦事項列表應用程式,Django 可能有點像用消防水管來進行水槍大戰。讓我們來看看它們是如何結合在一起的。
Django 將自己定位為“一個鼓勵快速開發和整潔、實用的設計的高階 Python Web 框架。它由經驗豐富的開發人員構建,解決了 Web 開發的很多麻煩,因此你可以專注於編寫應用程式而無需重新發明輪子”。而且它確實做到了!這個龐大的 Web 框架附帶了非常多的工具,以至於在開發過程中,如何將所有內容組合在一起協同工作可能是個謎。
除了框架本身很大,Django 社群也是非常龐大的。事實上,它非常龐大和活躍,以至於有一個網站專門用於為人們收集第三方包,這些第三方包可整合進 Django 來做一大堆事情。包括從身份驗證和授權到完全基於 Django 的內容管理系統,電子商務附加元件以及與 Stripe(LCTT 譯註:美版“支付寶”)整合的所有內容。至於不要重新發明輪子:如果你想用 Django 完成一些事情,有人可能已經做過了,你只需將它整合進你的專案就行。
為此,我們希望使用 Django 構建 REST API,因此我們將使用流行的 Django REST 框架。它的工作是將 Django 框架(Django 使用自己的模板引擎構建 HTML 頁面)轉換為專門用於有效地處理 REST 互動的系統。讓我們開始吧。
$ mkdir django_todo$ cd django_todo$ pipenv install --python 3.6$ pipenv shell(django-someHash) $ pipenv install django djangorestframework
作為參考,我們使用的是 django-2.0.7
和 djangorestframework-3.8.2
。
與 Flask, Tornado 和 Pyramid 不同,我們不需要自己編寫 setup.py
檔案,我們並不是在做一個可安裝的 Python 發布版。像很多事情一樣,Django 以自己的方式處理這個問題。我們仍然需要一個 requirements.txt
檔案來跟蹤我們在其它地方部署的所有必要安裝。但是,就 Django 專案中的目標模組而言,Django 會讓我們列出我們想要存取的子目錄,然後允許我們從這些目錄中匯入,就像它們是已安裝的包一樣。
首先,我們必須建立一個 Django 專案。
當我們安裝了 Django 後,我們還安裝了命令列指令碼 django-admin
。它的工作是管理所有與 Django 相關的命令,這些命令有助於我們將專案整合在一起,並在我們繼續開發的過程中對其進行維護。django-admin
並不是讓我們從頭開始構建整個 Django 生態系統,而是讓我們從標準 Django 專案所需的所有必要檔案(以及更多)的基礎上開始。
呼叫 django-admin
的 start-project
命令的語法是 django-admin startproject <專案名稱> <存放目錄>
。我們希望檔案存於當前的工作目錄中,所以:
(django-someHash) $ django-admin startproject django_todo .
輸入 ls
將顯示一個新檔案和一個新目錄。
(django-someHash) $ lsmanage.py django_todo
manage.py
是一個可執行命令列 Python 檔案,它最終成為 django-admin
的封裝。因此,它的工作與 django-admin
是一樣的:幫助我們管理專案。因此得名 manage.py
。
它在 django_todo
目錄裡建立了一個新目錄 django_todo
,其代表了我們專案的設定根目錄。現在讓我們深入研究一下。
可以將 django_todo
目錄稱為“設定根目錄”,我們的意思是這個目錄包含了通常設定 Django 專案所需的檔案。幾乎所有這個目錄之外的內容都只關注與專案模型、檢視、路由等相關的“業務邏輯”。所有連線專案的點都將在這裡出現。
在 django_todo
目錄中呼叫 ls
會顯示以下四個檔案:
(django-someHash) $ cd django_todo(django-someHash) $ ls__init__.py settings.py urls.py wsgi.py
__init__.py
檔案為空,之所以存在是為了將此目錄轉換為可匯入的 Python 包。settings.py
是設定大多數設定項的地方。例如專案是否處於 DEBUG 模式,正在使用哪些資料庫,Django 應該定位檔案的位置等等。它是設定根目錄的“主要設定”部分,我們將在一會深入研究。urls.py
顧名思義就是設定 URL 的地方。雖然我們不必在此檔案中顯式寫入專案的每個 URL,但我們需要讓此檔案知道在其他任何地方已宣告的 URL。如果此檔案未指向其它 URL,則那些 URL 就不存在。wsgi.py
用於在生產環境中提供應用程式。就像 Pyramid、 Tornado 和 Flask 暴露了一些 “app” 物件一樣,它們用來提供設定好的應用程式,Django 也必須暴露一個,就是在這裡完成的。它可以和 Gunicorn、Waitress 或者 uWSGI 一起配合來提供服務。看一看 settings.py
,它裡面有大量的設定項,那些只是預設值!這甚至不包括資料庫、靜態檔案、媒體檔案、任何整合的勾點,或者可以設定 Django 專案的任何其它幾種方式。讓我們從上到下看看有什麼:
BASE_DIR
設定目錄的絕對路徑,或者是 manage.py
所在的目錄。這對於定位檔案非常有用。SECRET_KEY
是用於 Django 專案中加密簽名的金鑰。在實際中,它用於對談、cookie、CSRF 保護和身份驗證令牌等。最好在第一次提交之前,盡快應該更改 SECRET_KEY
的值並將其放置到環境變數中。DEBUG
告訴 Django 是以開發模式還是生產模式執行專案。這是一個非常關鍵的區別。DEBUG
設定為 True
,這可能成為一個巨大的安全問題。DEBUG
設定為環境變數,如 bool(os.environ.get('DEBUG', ''))
。ALLOWED_HOSTS
是應用程式提供服務的主機名的列表。在開發模式中,這可能是空的;但是在生產環境中,如果為專案提供服務的主機不在 ALLOWED_HOSTS
列表中,Django 專案將無法執行。這是設定為環境變數的另一種情況。INSTALLED_APPS
是我們的 Django 專案可以存取的 Django “apps” 列表(將它們視為子目錄,稍後會詳細介紹)。預設情況下,它將提供:css
檔案、js
檔案、任何屬於我們網站設計的圖片等。MIDDLEWARE
顧名思義:幫助 Django 專案執行的中介軟體。其中很大一部分用於處理各種型別的安全,儘管我們可以根據需要新增其它中介軟體。ROOT_URLCONF
設定基本 URL 組態檔的匯入路徑。還記得我們之前見過的那個 urls.py
嗎?預設情況下,Django 指向該檔案以此來收集所有的 URL。如果我們想讓 Django 在其它地方尋找,我們將在這裡設定 URL 位置的匯入路徑。TEMPLATES
是 Django 用於我們網站前端的模板引擎列表,假如我們依靠 Django 來構建我們的 HTML。我們在這裡不需要,那就無關緊要了。WSGI_APPLICATION
設定我們的 WSGI 應用程式的匯入路徑 —— 在生產環境下使用的東西。預設情況下,它指向 wsgi.py
中的 application
物件。這很少(如果有的話)需要修改。DATABASES
設定 Django 專案將存取那些資料庫。必須設定 default
資料庫。我們可以通過名稱設定別的資料庫,只要我們提供 HOST
、USER
、PASSWORD
、PORT
、資料庫名稱 NAME
和合適的 ENGINE
。可以想象,這些都是敏感的資訊,因此最好將它們隱藏在環境變數中。檢視 Django 文件了解更多詳情。AUTH_PASSWORD_VALIDATORS
實際上是執行以檢查輸入密碼的函數列表。預設情況下我們有一些,但是如果我們有其它更複雜的驗證需求:不僅僅是檢查密碼是否與使用者的屬性匹配,是否超過最小長度,是否是 1000 個最常用的密碼之一,或者密碼完全是數位,我們可以在這裡列出它們。LANGUAGE_CODE
設定網站的語言。預設情況下它是美國英語,但我們可以將其切換為其它語言。TIME_ZONE
是我們 Django 專案後中自動生成的時間戳的時區。我強調堅持使用 UTC 並在其它地方執行任何特定於時區的處理,而不是嘗試重新設定此設定。正如這篇文章 所述,UTC 是所有時區的共同點,因為不需要擔心偏移。如果偏移很重要,我們可以根據需要使用與 UTC 的適當偏移來計算它們。USE_I18N
將讓 Django 使用自己的翻譯服務來為前端翻譯字串。I18N = 國際化(internationalization,“i” 和 “n” 之間共 18 個字元)。USE_L10N
L10N = 在地化(localization,在 l和
n之間共 10 個字元) 。如果設定為
True
,那麼將使用資料的公共本地格式。一個很好的例子是日期:在美國它是 MM-DD-YYYY。在歐洲,日期往往寫成 DD-MM-YYYY。STATIC_URL
是用於提供靜態檔案的主體部分。我們將構建一個 REST API,因此我們不需要考慮靜態檔案。通常,這會為每個靜態檔案的域名設定根路徑。所以,如果我們有一個 Logo 影象,那就是 http://<domainname>/<STATIC_URL>/logo.gif
。預設情況下,這些設定已準備就緒。我們必須改變的一個選項是 DATABASES
設定。首先,我們建立將要使用的資料庫:
(django-someHash) $ createdb django_todo
我們想要像使用 Flask、Pyramid 和 Tornado 一樣使用 PostgreSQL 資料庫,這意味著我們必須更改 DATABASES
設定以允許我們的伺服器存取 PostgreSQL 資料庫。首先是引擎。預設情況下,資料庫引擎是 django.db.backends.sqlite3
,我們把它改成 django.db.backends.postgresql
。
有關 Django 可用引擎的更多資訊,請檢視文件。請注意,儘管技術上可以將 NoSQL 解決方案整合到 Django 專案中,但為了開箱即用,Django 強烈偏向於 SQL 解決方案。
接下來,我們必須為連線引數的不同部分指定鍵值對。
NAME
是我們剛剛建立的資料庫的名稱。USER
是 Postgres 資料庫使用者名稱。PASSWORD
是存取資料庫所需的密碼。HOST
是資料庫的主機。當我們在本地開發時,localhost
或 127.0.0.1
都將起作用。PORT
是我們為 Postgres 開放的埠,它通常是 5432
。settings.py
希望我們為每個鍵提供字串值。但是,這是高度敏感的資訊。任何負責任的開發人員都不應該這樣做。有幾種方法可以解決這個問題,一種是我們需要設定環境變數。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME', ''), 'USER': os.environ.get('DB_USER', ''), 'PASSWORD': os.environ.get('DB_PASS', ''), 'HOST': os.environ.get('DB_HOST', ''), 'PORT': os.environ.get('DB_PORT', ''), }}
在繼續之前,請確保設定環境變數,否則 Django 將無法運作。此外,我們需要在此環境中安裝 psycopg2
,以便我們可以與資料庫通訊。
讓我們在這個專案中實現一些函數。我們將使用 Django REST 框架來構建 REST API,所以我們必須確保在 settings.py
中將 rest_framework
新增到 INSTALLED_APPS
的末尾。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework']
雖然 Django REST 框架並不專門需要基於類的檢視(如 Tornado)來處理傳入的請求,但類是編寫檢視的首選方法。讓我們來定義一個類檢視。
讓我們在 django_todo
建立一個名為 views.py
的檔案。在 views.py
中,我們將建立 “Hello, world!” 檢視。
# in django_todo/views.pyfrom rest_framework.response import JsonResponsefrom rest_framework.views import APIViewclass HelloWorld(APIView): def get(self, request, format=None): """Print 'Hello, world!' as the response body.""" return JsonResponse("Hello, world!")
每個 Django REST 框架基於類的檢視都直接或間接地繼承自 APIView
。APIView
處理大量的東西,但針對我們的用途,它做了以下特定的事情:
* 根據 HTTP 方法(例如 GET、POST、PUT、DELETE)來設定引導對應請求所需的方法* 用我們需要的所有資料和屬性來填充 `request` 物件,以便解析和處理傳入的請求 * 採用 `Response` 或 `JsonResponse`,每個排程方法(即名為 `get`、`post`、`put`、`delete` 的方法)返回並構造格式正確的 HTTP 響應。
終於,我們有一個檢視了!它本身沒有任何作用,我們需要將它連線到路由。
如果我們跳轉到 django_todo/urls.py
,我們會到達預設的 URL 組態檔。如前所述:如果 Django 專案中的路由不包含在此處,則它不存在。
我們在給定的 urlpatterns
列表中新增所需的 URL。預設情況下,我們有一整套 URL 用於 Django 的內建管理後端系統。我們會完全刪除它。
我們還得到一些非常有用的文件字串,它告訴我們如何向 Django 專案新增路由。我們需要呼叫 path()
,伴隨三個引數:
讓我們匯入 HelloWorld
檢視並將其附加到主路徑 /
。我們可以從 urlpatterns
中刪除 admin
的路徑,因為我們不會使用它。
# django_todo/urls.py, after the big doc stringfrom django.urls import pathfrom django_todo.views import HelloWorldurlpatterns = [ path('', HelloWorld.as_view(), name="hello"),]
好吧,這裡有一點不同。我們指定的路由只是一個空白字串,為什麼它會工作?Django 假設我們宣告的每個路由都以一個前導斜槓開頭,我們只是在初始域名後指定資源路由。如果一條路由沒有去往一個特定的資源,而只是一個主頁,那麼該路由是 ''
,實際上是“沒有資源”。
HelloWorld
檢視是從我們剛剛建立的 views.py
檔案匯入的。為了執行此匯入,我們需要更新 settings.py
中的 INSTALLED_APPS
列表使其包含 django_todo
。是的,這有點奇怪。以下是一種理解方式。
INSTALLED_APPS
指的是 Django 認為可匯入的目錄或包的列表。它是 Django 處理專案的各個元件的方式,比如安裝了一個包,而不需要經過 setup.py
的方式。我們希望將 django_todo
目錄視為可匯入的包,因此我們將該目錄包含在 INSTALLED_APPS
中。現在,在該目錄中的任何模組也是可匯入的。所以我們得到了我們的檢視。
path
函數只將檢視函數作為第二個引數,而不僅僅是基於類的檢視。幸運的是,所有有效的基於 Django 類的檢視都包含 .as_view()
方法。它的工作是將基於類的檢視的所有優點彙總到一個檢視函數中並返回該檢視函數。所以,我們永遠不必擔心轉換的工作。相反,我們只需要考慮業務邏輯,讓 Django 和 Django REST 框架處理剩下的事情。
讓我們在瀏覽器中開啟它!
Django 提供了自己的本地開發伺服器,可通過 manage.py
存取。讓我們切換到包含 manage.py
的目錄並輸入:
(django-someHash) $ ./manage.py runserverPerforming system checks...System check identified no issues (0 silenced).August 01, 2018 - 16:47:24Django version 2.0.7, using settings 'django_todo.settings'Starting development server at http://127.0.0.1:8000/Quit the server with CONTROL-C.
當 runserver
執行時,Django 會檢查以確保專案(或多或少)正確連線在一起。這不是萬無一失的,但確實會發現一些明顯的問題。如果我們的資料庫與程式碼不同步,它會通知我們。毫無疑問,因為我們沒有將任何應用程式的東西提交到我們的資料庫,但現在這樣做還是可以的。讓我們存取 http://127.0.0.1:8000
來檢視 HelloWorld
檢視的輸出。
咦?這不是我們在 Pyramid、Flask 和 Tornado 中看到的明文資料。當使用 Django REST 框架時,HTTP 響應(在瀏覽器中檢視時)是這樣呈現的 HTML,以紅色顯示我們的實際 JSON 響應。
但不要擔心!如果我們在命令列中使用 curl
快速存取 http://127.0.0.1:8000
,我們就不會得到任何花哨的 HTML,只有內容。
# 注意:在不同的終埠視窗中執行此操作,在虛擬環境之外$ curl http://127.0.0.1:8000"Hello, world!"
棒極了!
Django REST 框架希望我們在使用瀏覽器瀏覽時擁有一個人性化的介面。這是有道理的,如果在瀏覽器中檢視 JSON,通常是因為人們想要檢查它是否正確,或者在設計一些消費者 API 時想要了解 JSON 響應。這很像你從 Postman 中獲得的東西。
無論哪種方式,我們都知道我們的檢視工作了!酷!讓我們概括一下我們做過的事情:
django-admin startproject <專案名稱>
開始一個專案django_todo/settings.py
中的 DEBUG
、SECRET_KEY
,還有 DATABASES
字典INSTALLED_APPS
django_todo/views.py
來包含我們的第一個類檢視,它返回響應 “Hello, world!”django_todo/urls.py
,其中包含我們的根路由django_todo/settings.py
中更新 INSTALLED_APPS
以包含 django_todo
包現在讓我們來建立資料模型吧。
Django 專案的整個基礎架構都是圍繞資料模型構建的,它是這樣編寫的,每個資料模型夠可以擁有自己的小天地,擁有自己的檢視,自己與其資源相關的 URL 集合,甚至是自己的測試(如果我們想要的話)。
如果我們想構建一個簡單的 Django 專案,我們可以通過在 django_todo
目錄中編寫我們自己的 models.py
檔案並將其匯入我們的檢視來避免這種情況。但是,我們想以“正確”的方式編寫 Django 專案,因此我們應該盡可能地將模型拆分成符合 Django Way™(Django 風格)的包。
Django Way 涉及建立所謂的 Django “應用程式”,它本身並不是單獨的應用程式,它們沒有自己的設定和諸如此類的東西(雖然它們也可以)。但是,它們可以擁有一個人們可能認為屬於獨立應用程式的東西:
它們是獨立的,因此可以像獨立應用程式一樣輕鬆共用。實際上,Django REST 框架是 Django 應用程式的一個例子。它包含自己的檢視和 HTML 模板,用於提供我們的 JSON。我們只是利用這個 Django 應用程式將我們的專案變成一個全面的 RESTful API 而不用那麼麻煩。
要為我們的待辦事項列表項建立 Django 應用程式,我們將要使用 manage.py
的 startapp
命令。
(django-someHash) $ ./manage.py startapp todo
startapp
命令成功執行後沒有輸出。我們可以通過使用 ls
來檢查它是否完成它應該做的事情。
(django-someHash) $ lsPipfile Pipfile.lock django_todo manage.py todo
看看:我們有一個全新的 todo
目錄。讓我們看看裡面!
(django-someHash) $ ls todo__init__.py admin.py apps.py migrations models.py tests.py views.py
以下是 manage.py startapp
建立的檔案:
__init__.py
是空檔案。它之所以存在是因為此目錄可看作是模型、檢視等的有效匯入路徑。admin.py
不是空檔案。它用於在 Django admin 中規範化這個應用程式的模型,我們在本文中沒有涉及到它。apps.py
這裡基本不起作用。它有助於規範化 Django admin 的模型。migrations
是一個包含我們資料模型快照的目錄。它用於更新資料庫。這是少數幾個內建了資料庫管理的框架之一,其中一部分允許我們更新資料庫,而不必拆除它並重建它以更改 Schema。models.py
是資料模型所在。tests.py
是測試所在的地方,如果我們需要寫測試。views.py
用於我們編寫的與此應用程式中的模型相關的檢視。它們不是一定得寫在這裡。例如,我們可以在 django_todo/views.py
中寫下我們所有的檢視。但是,它在這個應用程式中更容易將我們的概念理清。在覆蓋了許多概念的擴充套件應用程式的關係之間會變得更加密切。它並沒有為這個應用程式建立 urls.py
檔案,但我們可以自己建立。
(django-someHash) $ touch todo/urls.py
在繼續之前,我們應該幫自己一個忙,將這個新 Django 應用程式新增到 django_todo/settings.py
中的 INSTALLED_APPS
列表中。
# settings.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'django_todo', 'todo' # <--- 新增了這行]
檢查 todo/models.py
發現 manage.py
已經為我們編寫了一些程式碼。不同於在 Flask、Tornado 和 Pyramid 實現中建立模型的方式,Django 不利用第三方來管理資料庫對談或構建其物件範例。它全部歸入 Django 的 django.db.models
子模組。
然而,建立模型的方式或多或少是相同的。要在 Django 中建立模型,我們需要構建一個繼承自 models.Model
的 class
,將應用於該模型範例的所有欄位都應視為類屬性。我們不像過去那樣從 SQLAlchemy 匯入列和欄位型別,而是直接從 django.db.models
匯入。
# todo/models.pyfrom django.db import modelsclass Task(models.Model): """Tasks for the To Do list.""" name = models.CharField(max_length=256) note = models.TextField(blank=True, null=True) creation_date = models.DateTimeField(auto_now_add=True) due_date = models.DateTimeField(blank=True, null=True) completed = models.BooleanField(default=False)
雖然 Django 的需求和基於 SQLAlchemy 的系統之間存在一些明顯的差異,但總體內容和結構或多或少相同。讓我們來指出這些差異。
我們不再需要為物件範例宣告自動遞增 ID 的單獨欄位。除非我們指定一個不同的欄位作為主鍵,否則 Django 會為我們構建一個。
我們只是直接參照資料型別作為列本身,而不是範例化傳遞資料型別物件的 Column
物件。
Unicode
欄位變為 models.CharField
或 models.TextField
。CharField
用於特定最大長度的小文字欄位,而 TextField
用於任何數量的文字。
TextField
應該是空白的,我們以兩種方式指定它。blank = True
表示當構建此模型的範例,並且正在驗證附加到該欄位的資料時,該資料是可以為空的。這與 null = True
不同,後者表示當構造此模型類的表時,對應於 note
的列將允許空白或為 NULL
。因此,總而言之,blank = True
控制如何將資料新增到模型範例,而 null = True
控制如何構建儲存該資料的資料庫表。
DateTime
欄位增加了一些屬性,並且能夠為我們做一些工作,使得我們不必修改類的 __init__
方法。對於 creation_date
欄位,我們指定 auto_now_add = True
。在實際意義上意味著,當建立一個新模型範例時,Django 將自動記錄現在的日期和時間作為該欄位的值。這非常方便!
當 auto_now_add
及其類似屬性 auto_now
都沒被設定為 True
時,DateTimeField
會像其它欄位一樣需要預期的資料。它需要提供一個適當的 datetime
物件才能生效。due_date
列的 blank
和 null
屬性都設定為 True
,這樣待辦事項列表中的專案就可以成為將來某個時間點完成,沒有確定的日期或時間。
BooleanField
最終可以取兩個值:True
或 False
。這裡,預設值設定為 False
。
如前所述,Django 有自己的資料庫管理方式。我們可以利用 Django 提供的 manage.py
指令碼,而不必編寫任何關於資料庫的程式碼。它不僅可以管理我們資料庫的表的構建,還可以管理我們希望對這些表進行的任何更新,而不必將整個事情搞砸!
因為我們構建了一個新模型,所以我們需要讓資料庫知道它。首先,我們需要將與此模型對應的模式放入程式碼中。manage.py
的 makemigrations
命令對我們構建的模型類及其所有欄位進行快照。它將獲取該資訊並將其打包成一個 Python 指令碼,該指令碼將存在於特定 Django 應用程式的 migrations
目錄中。永遠沒有理由直接執行這個遷移指令碼。它的存在只是為了讓 Django 可以使用它作為更新資料庫表的基礎,或者在我們更新模型類時繼承資訊。
(django-someHash) $ ./manage.py makemigrationsMigrations for 'todo': todo/migrations/0001_initial.py - Create model Task
這將查詢 INSTALLED_APPS
中列出的每個應用程式,並檢查這些應用程式中存在的模型。然後,它將檢查相應的 migrations
目錄中的遷移檔案,並將它們與每個 INSTALLED_APPS
中的模型進行比較。如果模型已經升級超出最新遷移所應存在的範圍,則將建立一個繼承自最新遷移檔案的新遷移檔案,它將自動命名,並且還會顯示一條訊息,說明自上次遷移以來發生了哪些更改。
如果你上次處理 Django 專案已經有一段時間了,並且不記得模型是否與遷移同步,那麼你無需擔心。makemigrations
是一個冪等操作。無論你執行 makemigrations
一次還是 20 次,migrations
目錄只有一個與當前模型設定的副本。更棒的是,當我們執行 ./manage.py runserver
時,Django 檢測到我們的模型與遷移不同步,它會用彩色文字告訴我們以便我們可以做出適當的選擇。
下一個要點是至少讓每個人存取一次:建立一個遷移檔案不會立即影響我們的資料庫。當我們執行 makemigrations
時,我們佈置我們的 Django 專案定義了給定的表應該如何建立和最終查詢。我們仍要將這些更改應用於資料庫。這就是 migrate
命令的用途。
(django-someHash) $ ./manage.py migrateOperations to perform: Apply all migrations: admin, auth, contenttypes, sessions, todoRunning migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying sessions.0001_initial... OK Applying todo.0001_initial... OK
當我們應用這些遷移時,Django 首先檢查其他 INSTALLED_APPS
是否有需要應用的遷移,它大致按照列出的順序檢查它們。我們希望我們的應用程式最後列出,因為我們希望確保,如果我們的模型依賴於任何 Django 的內建模型,我們所做的資料庫更新不會受到依賴性問題的影響。
我們還有另一個要構建的模型:User
模型。但是,因為我們正在使用 Django,事情有一些變化。許多應用程式需要某種型別的使用者模型,Django 的 django.contrib.auth
包構建了自己的使用者模型供我們使用。如果無需使用者所需要的身份驗證令牌,我們可以繼續使用它而不是重新發明輪子。
但是,我們需要那個令牌。我們可以通過兩種方式來處理這個問題。
User
物件,我們自己的物件通過新增 token
欄位來擴充套件它User
物件一對一關係的新物件,其唯一目的是持有一個令牌我習慣於建立物件關係,所以讓我們選擇第二種選擇。我們稱之為 Owner
,因為它基本上具有與 User
類似的內涵,這就是我們想要的。
出於純粹的懶惰,我們可以在 todo/models.py
中包含這個新的 Owner
物件,但是不要這樣做。Owner
沒有明確地與任務列表上的專案的建立或維護有關。從概念上講,Owner
只是任務的所有者。甚至有時候我們想要擴充套件這個 Owner
以包含與任務完全無關的其他資料。
為了安全起見,讓我們建立一個 owner
應用程式,其工作是容納和處理這個 Owner
物件。
(django-someHash) $ ./manage.py startapp owner
不要忘記在 settings.py
檔案中的 INSTALLED_APPS
中新增它。 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'django_todo', 'todo', 'owner']
如果我們檢視 Django 專案的根目錄,我們現在有兩個 Django 應用程式:
(django-someHash) $ lsPipfile Pipfile.lock django_todo manage.py owner todo
在 owner/models.py
中,讓我們構建這個 Owner
模型。如前所述,它與 Django 的內建 User
物件有一對一的關係。我們可以用 Django 的 models.OneToOneField
強制實現這種關係。
# owner/models.pyfrom django.db import modelsfrom django.contrib.auth.models import Userimport secretsclass Owner(models.Model): """The object that owns tasks.""" user = models.OneToOneField(User, on_delete=models.CASCADE) token = models.CharField(max_length=256) def __init__(self, *args, **kwargs): """On construction, set token.""" self.token = secrets.token_urlsafe(64) super().__init__(*args, **kwargs)
這表示 Owner
物件對應到 User
物件,每個 user
範例有一個 owner
範例。on_delete = models.CASCADE
表示如果相應的 User
被刪除,它所對應的 Owner
範例也將被刪除。讓我們執行 makemigrations
和 migrate
來將這個新模型放入到我們的資料庫中。
(django-someHash) $ ./manage.py makemigrationsMigrations for 'owner': owner/migrations/0001_initial.py - Create model Owner(django-someHash) $ ./manage.py migrateOperations to perform: Apply all migrations: admin, auth, contenttypes, owner, sessions, todoRunning migrations: Applying owner.0001_initial... OK
現在我們的 Owner
需要擁有一些 Task
物件。它與上面看到的 OneToOneField
非常相似,只不過我們會在 Task
物件上貼一個 ForeignKey
欄位指向 Owner
。
# todo/models.pyfrom django.db import modelsfrom owner.models import Ownerclass Task(models.Model): """Tasks for the To Do list.""" name = models.CharField(max_length=256) note = models.TextField(blank=True, null=True) creation_date = models.DateTimeField(auto_now_add=True) due_date = models.DateTimeField(blank=True, null=True) completed = models.BooleanField(default=False) owner = models.ForeignKey(Owner, on_delete=models.CASCADE)
每個待辦事項列表任務只有一個可以擁有多個任務的所有者。刪除該所有者後,他們擁有的任務都會隨之刪除。
現在讓我們執行 makemigrations
來獲取我們的資料模型設定的新快照,然後執行 migrate
將這些更改應用到我們的資料庫。
(django-someHash) django $ ./manage.py makemigrationsYou are trying to add a non-nullable field 'owner' to task without a default; we can't do that (the database needs something to populate existing rows).Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py
不好了!出現了問題!發生了什麼?其實,當我們建立 Owner
物件並將其作為 ForeignKey
新增到 Task
時,要求每個 Task
都需要一個 Owner
。但是,我們為 Task
物件進行的第一次遷移不包括該要求。因此,即使我們的資料庫表中沒有資料,Django 也會對我們的遷移進行預先檢查,以確保它們相容,而我們提議的這種新遷移不是。
有幾種方法可以解決這類問題:
Task
物件的 owner
欄位owner
欄位設定 NULL
值方案 2 在這裡沒有多大意義。我們建議,預設情況下,任何建立的 Task
都會對應到某個預設所有者,儘管預設所有者不一定存在。 方案 1 要求我們銷毀和重建我們的遷移,而我們應該把它們留下。
讓我們考慮選項 3。在這種情況下,如果我們允許 Task
表為所有者提供空值,它不會很糟糕。從這一點開始建立的任何任務都必然擁有一個所有者。如果你的資料庫表並非不能接受重新架構,請刪除該遷移、刪除表並重建遷移。
# todo/models.pyfrom django.db import modelsfrom owner.models import Ownerclass Task(models.Model): """Tasks for the To Do list.""" name = models.CharField(max_length=256) note = models.TextField(blank=True, null=True) creation_date = models.DateTimeField(auto_now_add=True) due_date = models.DateTimeField(blank=True, null=True) completed = models.BooleanField(default=False) owner = models.ForeignKey(Owner, on_delete=models.CASCADE, null=True)(django-someHash) $ ./manage.py makemigrationsMigrations for 'todo': todo/migrations/0002_task_owner.py - Add field owner to task(django-someHash) $ ./manage.py migrateOperations to perform: Apply all migrations: admin, auth, contenttypes, owner, sessions, todoRunning migrations: Applying todo.0002_task_owner... OK
酷!我們有模型了!歡迎使用 Django 宣告物件的方式。
出於更好的權衡,讓我們確保無論何時製作 User
,它都會自動與新的 Owner
物件對應。我們可以使用 Django 的 signals
系統來做到這一點。基本上,我們確切地表達了意圖:“當我們得到一個新的 User
被構造的信號時,構造一個新的 Owner
並將新的 User
設定為 Owner
的 user
欄位。”在實踐中看起來像這樣:
# owner/models.pyfrom django.contrib.auth.models import Userfrom django.db import modelsfrom django.db.models.signals import post_savefrom django.dispatch import receiverimport secretsclass Owner(models.Model): """The object that owns tasks.""" user = models.OneToOneField(User, on_delete=models.CASCADE) token = models.CharField(max_length=256) def __init__(self, *args, **kwargs): """On construction, set token.""" self.token = secrets.token_urlsafe(64) super().__init__(*args, **kwargs)@receiver(post_save, sender=User)def link_user_to_owner(sender, **kwargs): """If a new User is saved, create a corresponding Owner.""" if kwargs['created']: owner = Owner(user=kwargs['instance']) owner.save()
我們設定了一個函數,用於監聽從 Django 中內建的 User
物件傳送的信號。它正在等待 User
物件被儲存之後的情況。這可以來自新的 User
或對現有 User
的更新。我們在監聽功能中辨別出兩種情況。
如果傳送信號的東西是新建立的範例,kwargs ['created']
將具有值 True
。如果是 True
的話,我們想做點事情。如果它是一個新範例,我們建立一個新的 Owner
,將其 user
欄位設定為建立的新 User
範例。之後,我們 save()
新的 Owner
。如果一切正常,這將提交更改到資料庫。如果資料沒通過我們宣告的欄位的驗證,它將失敗。
現在讓我們談談我們將如何存取資料。
在 Flask、Pyramid 和 Tornado 框架中,我們通過對某些資料庫對談執行查詢來存取模型資料。也許它被附加到 request
物件,也許它是一個獨立的 session
物件。無論如何,我們必須建立與資料庫的實時連線並在該連線上進行查詢。
這不是 Django 的工作方式。預設情況下,Django 不利用任何第三方物件關係對映(ORM)與資料庫進行通訊。相反,Django 允許模型類維護自己與資料庫的對話。
從 django.db.models.Model
繼承的每個模型類都會附加一個 objects
物件。這將取代我們熟悉的 session
或 dbsession
。讓我們開啟 Django 給我們的特殊 shell,並研究這個 objects
物件是如何工作的。
(django-someHash) $ ./manage.py shellPython 3.7.0 (default, Jun 29 2018, 20:13:13)[Clang 9.1.0 (clang-902.0.39.2)] on darwinType "help", "copyright", "credits" or "license" for more information.(InteractiveConsole)>>>
Django shell 與普通的 Python shell 不同,因為它知道我們正在構建的 Django 專案,可以輕鬆匯入我們的模型、檢視、設定等,而不必擔心安裝包。我們可以通過簡單的 import
存取我們的模型。
>>> from owner.models import Owner>>> Owner<class 'owner.models.Owner'>
目前,我們沒有 Owner
範例。我們可以通過 Owner.objects.all()
查詢它們。
>>> Owner.objects.all()<QuerySet []>
無論何時我們在 <Model> .objects
物件上執行查詢方法,我們都會得到 QuerySet
。為了我們的目的,它實際上是一個列表,這個列表向我們顯示它是空的。讓我們通過建立一個 User
來建立一個 Owner
。
>>> from django.contrib.auth.models import User>>> new_user = User(username='kenyattamurphy', email='[email protected]')>>> new_user.set_password('wakandaforever')>>> new_user.save()
如果我們現在查詢所有的 Owner
,我們應該會找到 Kenyatta。
>>> Owner.objects.all()<QuerySet [<Owner: Owner object (1)>]>
棒極了!我們得到了資料!
我們將在 “Hello World” 之外來回傳遞資料。因此,我們希望看到某種類似於 JSON 型別的輸出,它可以很好地表示資料。獲取該物件的資料並將其轉換為 JSON 物件以通過 HTTP 提交是資料序列化的一種方式。在序列化資料時,我們正在獲取我們目前擁有的資料並重新格式化以適應一些標準的、更易於理解的形式。
如果我用 Flask、Pyramid 和 Tornado 這樣做,我會在每個模型上建立一個新方法,讓使用者可以直接呼叫 to_json()
。to_json()
的唯一工作是返回一個 JSON 可序列化的(即數位、字串、列表、字典)字典,其中包含我想要為所討論的物件顯示的任何欄位。
對於 Task
物件,它可能看起來像這樣:
class Task(Base): ...all the fields... def to_json(self): """Convert task attributes to a JSON-serializable dict.""" return { 'id': self.id, 'name': self.name, 'note': self.note, 'creation_date': self.creation_date.strftime('%m/%d/%Y %H:%M:%S'), 'due_date': self.due_date.strftime('%m/%d/%Y %H:%M:%S'), 'completed': self.completed, 'user': self.user_id }
這不花哨,但它確實起到了作用。
然而,Django REST 框架為我們提供了一個物件,它不僅可以為我們這樣做,還可以在我們想要建立新物件範例或更新現有範例時驗證輸入,它被稱為 ModelSerializer。
Django REST 框架的 ModelSerializer
是我們模型的有效文件。如果沒有附加模型,它們就沒有自己的生命(因為那裡有 Serializer 類)。它們的主要工作是準確地表示我們的模型,並在我們的模型資料需要序列化並通過線路傳送時,將其轉換為 JSON。
Django REST 框架的 ModelSerializer
最適合簡單物件。舉個例子,假設我們在 Task
物件上沒有 ForeignKey
。我們可以為 Task
建立一個序列化器,它將根據需要將其欄位值轉換為 JSON,宣告如下:
# todo/serializers.pyfrom rest_framework import serializersfrom todo.models import Taskclass TaskSerializer(serializers.ModelSerializer): """Serializer for the Task model.""" class Meta: model = Task fields = ('id', 'name', 'note', 'creation_date', 'due_date', 'completed')
在我們新的 TaskSerializer
中,我們建立了一個 Meta
類。Meta
的工作就是儲存關於我們試圖序列化的東西的資訊(或後設資料)。然後,我們會注意到要顯示的特定欄位。如果我們想要顯示所有欄位,我們可以簡化過程並使用 __all __
。或者,我們可以使用 exclude
關鍵字而不是 fields
來告訴 Django REST 框架我們想要除了少數幾個欄位以外的每個欄位。我們可以擁有盡可能多的序列化器,所以也許我們想要一個用於一小部分欄位,而另一個用於所有欄位?在這裡都可以。
在我們的例子中,每個 Task
和它的所有者 Owner
之間都有一個關係,必須在這裡反映出來。因此,我們需要借用 serializers.PrimaryKeyRelatedField
物件來指定每個 Task
都有一個 Owner
,並且該關係是一對一的。它的所有者將從已有的全部所有者的集合中找到。我們通過對這些所有者進行查詢並返回我們想要與此序列化程式關聯的結果來獲得該集合:Owner.objects.all()
。我們還需要在欄位列表中包含 owner
,因為我們總是需要一個與 Task
相關聯的 Owner
。
# todo/serializers.pyfrom rest_framework import serializersfrom todo.models import Taskfrom owner.models import Ownerclass TaskSerializer(serializers.ModelSerializer): """Serializer for the Task model.""" owner = serializers.PrimaryKeyRelatedField(queryset=Owner.objects.all()) class Meta: model = Task fields = ('id', 'name', 'note', 'creation_date', 'due_date', 'completed', 'owner')
現在構建了這個序列化器,我們可以將它用於我們想要為我們的物件做的所有 CRUD 操作:
GET
一個特定的 Task
的 JSON 型別版本,我們可以做 TaskSerializer((some_task).data
POST
來建立一個新的 Task
,我們可以使用 TaskSerializer(data = new_data).save()
PUT
更新一些現有資料,我們可以用 TaskSerializer(existing_task, data = data).save()
我們沒有包括 delete
,因為我們不需要對 delete
操作做任何事情。如果你可以刪除一個物件,只需使用 object_instance.delete()
。
以下是一些序列化資料的範例:
>>> from todo.models import Task>>> from todo.serializers import TaskSerializer>>> from owner.models import Owner>>> from django.contrib.auth.models import User>>> new_user = User(username='kenyatta', email='[email protected]')>>> new_user.save_password('wakandaforever')>>> new_user.save() # creating the User that builds the Owner>>> kenyatta = Owner.objects.first() # 找到 kenyatta 的所有者>>> new_task = Task(name="Buy roast beef for the Sunday potluck", owner=kenyatta)>>> new_task.save()>>> TaskSerializer(new_task).data{'id': 1, 'name': 'Go to the supermarket', 'note': None, 'creation_date': '2018-07-31T06:00:25.165013Z', 'due_date': None, 'completed': False, 'owner': 1}
使用 ModelSerializer
物件可以做更多的事情,我建議檢視文件以獲得更強大的功能。否則,這就是我們所需要的。現在是時候深入檢視了。
我們已經構建了模型和序列化器,現在我們需要為我們的應用程式設定檢視和 URL。畢竟,對於沒有檢視的應用程式,我們無法做任何事情。我們已經看到了上面的 HelloWorld
檢視的範例。然而,這總是一個人為的、概念驗證的例子,並沒有真正展示 Django REST 框架的檢視可以做些什麼。讓我們清除 HelloWorld
檢視和 URL,這樣我們就可以從我們的檢視重新開始。
我們要構建的第一個檢視是 InfoView
。與之前的框架一樣,我們只想打包並行送一個我們用到的路由的字典。檢視本身可以存在於 django_todo.views
中,因為它與特定模型無關(因此在概念上不屬於特定應用程式)。
# django_todo/views.pyfrom rest_framework.response import JsonResponsefrom rest_framework.views import APIViewclass InfoView(APIView): """List of routes for this API.""" def get(self, request): output = { 'info': 'GET /api/v1', 'register': 'POST /api/v1/accounts', 'single profile detail': 'GET /api/v1/accounts/<username>', 'edit profile': 'PUT /api/v1/accounts/<username>', 'delete profile': 'DELETE /api/v1/accounts/<username>', 'login': 'POST /api/v1/accounts/login', 'logout': 'GET /api/v1/accounts/logout', "user's tasks": 'GET /api/v1/accounts/<username>/tasks', "create task": 'POST /api/v1/accounts/<username>/tasks', "task detail": 'GET /api/v1/accounts/<username>/tasks/<id>', "task update": 'PUT /api/v1/accounts/<username>/tasks/<id>', "delete task": 'DELETE /api/v1/accounts/<username>/tasks/<id>' } return JsonResponse(output)
這與我們在 Tornado 中所擁有的完全相同。讓我們將它放置到合適的路由並繼續。為了更好的測試,我們還將刪除 admin/
路由,因為我們不會在這裡使用 Django 管理後端。
# in django_todo/urls.pyfrom django_todo.views import InfoViewfrom django.urls import pathurlpatterns = [ path('api/v1', InfoView.as_view(), name="info"),]
讓我們弄清楚下一個 URL,它將是建立新的 Task
或列出使用者現有任務的入口。這應該存在於 todo
應用程式的 urls.py
中,因為它必須專門處理 Task
物件而不是整個專案的一部分。
# in todo/urls.pyfrom django.urls import pathfrom todo.views import TaskListViewurlpatterns = [ path('', TaskListView.as_view(), name="list_tasks")]
這個路由處理的是什麼?我們根本沒有指定特定使用者或路徑。由於會有一些路由需要基本路徑 /api/v1/accounts/<username>/tasks
,為什麼我們只需寫一次就能一次又一次地寫它?
Django 允許我們用一整套 URL 並將它們匯入 django_todo/urls.py
檔案。然後,我們可以為這些匯入的 URL 中的每一個提供相同的基本路徑,只關心可變部分,你知道它們是不同的。
# in django_todo/urls.pyfrom django.urls import include, pathfrom django_todo.views import InfoViewurlpatterns = [ path('api/v1', InfoView.as_view(), name="info"), path('api/v1/accounts/<str:username>/tasks', include('todo.urls'))]
現在,來自 todo/urls.py
的每個 URL 都將以路徑 api/v1/accounts/<str:username>/tasks
為字首。
讓我們在 todo/views.py
中構建檢視。
# todo/views.pyfrom django.shortcuts import get_object_or_404from rest_framework.response import JsonResponsefrom rest_framework.views import APIViewfrom owner.models import Ownerfrom todo.models import Taskfrom todo.serializers import TaskSerializerclass TaskListView(APIView): def get(self, request, username, format=None): """Get all of the tasks for a given user.""" owner = get_object_or_404(Owner, user__username=username) tasks = Task.objects.filter(owner=owner).all() serialized = TaskSerializer(tasks, many=True) return JsonResponse({ 'username': username, 'tasks': serialized.data })
這裡一點程式碼裡面有許多要說明的,讓我們來看看吧。
我們從與我們一直使用的 APIView
的繼承開始,為我們的檢視奠定基礎。我們覆蓋了之前覆蓋的相同 get
方法,新增了一個引數,允許我們的檢視從傳入的請求中接收 username
。
然後我們的 get
方法將使用 username
來獲取與該使用者關聯的 Owner
。這個 get_object_or_404
函數允許我們這樣做,新增一些特殊的東西以方便使用。
如果無法找到指定的使用者,那麼查詢任務是沒有意義的。實際上,我們想要返回 404 錯誤。get_object_or_404
根據我們傳入的任何條件獲取單個物件,並返回該物件或引發 Http 404 異常。我們可以根據物件的屬性設定該條件。Owner
物件都通過 user
屬性附加到 User
。但是,我們沒有要搜尋的 User
物件,我們只有一個 username
。所以,當你尋找一個 Owner
時,我們對 get_object_or_404
說:通過指定 user__username
(這是兩個下劃線)來檢查附加到它的 User
是否具有我想要的 username
。通過 QuerySet
過濾時,這兩個下劃線表示 “此巢狀物件的屬性”。這些屬性可以根據需要進行深度巢狀。
我們現在擁有與給定使用者名稱相對應的 Owner
。我們使用 Owner
來過濾所有任務,只用 Task.objects.filter
檢索它擁有的任務。我們可以使用與 get_object_or_404
相同的巢狀屬性模式來鑽入連線到 Tasks
的 Owner
的 User
(tasks = Task.objects.filter(owner__user__username = username)).all()
),但是沒有必要那麼寬鬆。
Task.objects.filter(owner = owner).all()
將為我們提供與我們的查詢匹配的所有 Task
物件的QuerySet
。很棒。然後,TaskSerializer
將獲取 QuerySet
及其所有資料以及 many = True
標誌,以通知其為專案集合而不是僅僅一個專案,並返回一系列序列化結果。實際上是一個詞典列表。最後,我們使用 JSON 序列化資料和用於查詢的使用者名稱提供傳出響應。
post
方法看起來與我們之前看到的有些不同。
# still in todo/views.py# ...other imports...from rest_framework.parsers import JSONParserfrom datetime import datetimeclass TaskListView(APIView): def get(self, request, username, format=None): ... def post(self, request, username, format=None): """Create a new Task.""" owner = get_object_or_404(Owner, user__username=username) data = JSONParser().parse(request) data['owner'] = owner.id if data['due_date']: data['due_date'] = datetime.strptime(data['due_date'], '%d/%m/%Y %H:%M:%S') new_task = TaskSerializer(data=data) if new_task.is_valid(): new_task.save() return JsonResponse({'msg': 'posted'}, status=201) return JsonResponse(new_task.errors, status=400)
當我們從用戶端接收資料時,我們使用 JSONParser().parse(request)
將其解析為字典。我們將所有者新增到資料中並格式化任務的 due_date
(如果存在)。
我們的 TaskSerializer
完成了繁重的任務。它首先接收傳入的資料並將其轉換為我們在模型上指定的欄位。然後驗證該資料以確保它適合指定的欄位。如果附加到新 Task
的資料有效,它將使用該資料構造一個新的 Task
物件並將其提交給資料庫。然後我們發回適當的“耶!我們做了一件新東西!”響應。如果沒有,我們收集 TaskSerializer
生成的錯誤,並將這些錯誤傳送回用戶端,並返回 400 Bad Request
狀態程式碼。
如果我們要構建 put
檢視來更新 Task
,它看起來會非常相似。主要區別在於,當我們範例化 TaskSerializer
時,我們將傳遞舊物件和該物件的新資料,如 TaskSerializer(existing_task,data = data)
。我們仍然會進行有效性檢查並行回我們想要發回的響應。
Django 作為一個框架是高度可客製化的,每個人都有自己打造 Django 專案的方式。我在這裡寫出來的方式不一定是 Django 建立專案的確切方式。它只是 a) 我熟悉的方式,以及 b) 利用 Django 的管理系統。當你將概念切分到不同的小塊時,Django 專案的複雜性會增加。這樣做是為了讓多個人更容易為整個專案做出貢獻,而不會麻煩彼此。
然而,作為 Django 專案的大量檔案對映並不能使其更高效或自然地偏向於微服務架構。相反,它很容易成為一個令人困惑的獨石應用,這可能對你的專案仍然有用,它也可能使你的專案難以管理,尤其是隨著專案的增長。
仔細考慮你的需求並使用合適的工具來完成正確的工作。對於像這樣的簡單專案,Django 可能不是合適的工具。
Django 旨在處理多種模型,這些模型涵蓋了不同的專案領域,但它們可能有一些共同點。這個專案是一個小型的雙模型專案,有一些路由。即便我們把它構建更複雜,也只有七條路由,而仍然只是相同的兩個模型。這還不足以證明一個完整的 Django 專案。
如果我們期望這個專案能夠拓展,那麼將會是一個很好的選擇。這不是那種專案。這就是使用火焰噴射器來點燃蠟燭,絕對是大材小用了。
儘管如此,Web 框架就是一個 Web 框架,無論你使用哪個框架。它都可以接收請求並做出任何響應,因此你可以按照自己的意願進行操作。只需要注意你選擇的框架所帶來的開銷。
就是這樣!我們已經到了這個系列的最後!我希望這是一次啟發性的冒險。當你在考慮如何構建你的下一個專案時,它將幫助你做出的不僅僅是最熟悉的選擇。請務必閱讀每個框架的文件,以擴充套件本系列中涉及的任何內容(因為它沒有那麼全面)。每個人都有一個廣闊的世界。愉快地寫程式碼吧!