Django是一個開放源代碼的Web應用框架,由Python寫成。它最初是被開發來用於管理勞倫斯出版集團旗下的一些以新聞內容為主的網站的。Django的主要目標是使得開發複雜的、數據庫驅動的網站變得簡單。
對於Django 框架,我的評價是:這是一個全功能的Web 開發框架。Web 開發所需要的一切它都包含了,你不需要去選擇,只需要去熟悉,然後使用。
相對於 Flask、web.py 和 Bottle 這一類微型框架來說,Django 上手會有點複雜,但也並不難,因為官網的新手指導寫得很清晰。在眾多框架中,Django 的文檔算是相當優秀和完整的了。
下面,我們用Django簡單做一個系統出來。一起來熟悉 Django,瞭解它所提供的具體功能點。
另外,最重要的一個事情是如何查看文檔。Django 的文檔即便是寫得再好,也會讓人“迷路”。之後我們會快速地把Django 文檔中涉及的各個模塊都實踐一下,最終做出一個簡單的系統來。
在正式開發流程中,有可能會在選型階段來完成這部分內容。我們需要做一個簡單的系統,找找感覺,或者說看看實際使用中的匹配程度。
好了,讓我們開始吧。
如何閱讀 Django 文檔
用文字來描述如何閱讀文檔似乎不是一件容易的事,我儘量表達清楚。
文檔結構
Django 是基於 MVC(Model-View-Controller)模式的框架,雖然也有人稱其為 MTV 模式的,但是概念大同小異。我們只需知道,無論它是 MVC 模式還是 MTV 模式,甚至是其他什麼模式,最終的目的都是解耦,把一個軟件系統劃分為一層一層的結構,讓每一層的邏輯更加純粹,便於開發人員維護。
我們先看一下 Django 處理請求的示意圖(如圖 3-1 所示),從中可以直觀瞭解到 Django 提供了哪些模塊以及各個模塊所處的位置。
基於圖3-1,我們再來看看Django 文檔。
從大的劃分上來說,Django 文檔先是分出了這麼幾個部分:Model 層、View 層、模板層和Form模塊。剩下的部分都是功能文檔,比如Pagination(分頁模塊)和Caching(緩存模塊)等,這些是可以貫穿所有層的模塊。
而每個模塊或者說層又分為不同的模塊,下面我們簡要介紹一下常用的模塊。
1. Model 部分
Model 在整個項目結構中是直接同數據庫打交道的一層,所以數據處理的部分都在這一層。在業務開發中,關於純數據操作的部分,建議都放到這一層來做。
- Models:模型定義相關的使用說明,字段類型,meta 配置。
- QuerySets:在Model 的基礎上怎麼查看數據,有哪些接口可以用(比如all 和filter等),以及如何更進一步進行定製。畢竟ORM 在查詢上會有一些限制,但是在這一部分中你可以找到如何自定義查詢。
- Model instances:Model 的實例,一個實例可以理解為表中的一條記錄。這個實例有哪些操作,如何修改表的數據,都在這了。
- Migrations:在開發階段,我們可能會不斷調整表的結構,這部分就是用來做表結構調整的。理論上,我們只需要知道兩個命令—— makemigrations 和migrate 就行了,但是如果你想了解更多,可以仔細看看這部分內容。
- Advanced:高級部分(別被“高級”這兩字嚇到)。這裡涉及如何自定義Manager(也就是常用的Model.objects.all 中的objects),以及如果不爽ORM的查詢限制,但是又想使用ORM對象的映射,可以考慮使用原生SQL。另外,關於事務、聚合、搜索以及多數據庫支持等更多關於Model 層的需求,都可以看這裡。
- Other:這一部分有兩塊建議一定要看看。一個是Legacy databases(遺留數據庫)。想象一下,有人甩給你一個遺留的CMS 項目,要將其改成Django 的,你拿到數據庫後,可以直接根據此數據庫生成Model。之後你再花幾分鐘寫寫admin 部分的代碼,CMS 就出來了,是不是很高效!另外一部分就是Optimize database access,一定要看!避免你踩坑。如果不想看英文,可以到我的博客中看中文翻譯。雖然版本較老,但是依然適用,詳見“翻譯了Django 1.4 數據庫訪問優化部分”,網址為https://www.the5fire.com/django-database-access-optimization.html
2. View 部分
在上一節的文檔中,我們知道如何跟數據庫打交道,簡單來說就是可以操作數據庫了。而在這部分(View)中,側重點將在業務上。通過獲取數據、過濾數據和整合數據,拿到我們想要的結果,然後傳遞到模板中,最終通過HttpResponse 渲染出來。
在Django 的文檔中,View 部分包含了URL 配置、HTTP Request、HTTP Response 以及處理請求的view 函數和類級的View 等。下面我們一一列舉。
- The basics:包含URL 配置、View 方法以及常用裝飾器,比如想給這個接口增加緩存或者增加限制(只允許GET 請求)等。
- Reference:一些參考,包含內置的View(比如靜態文件處理和404 頁面處理等),Request 和Response 對象介紹,TemplateResponse 對象介紹。
- File uploads:這部分介紹了文件上傳的內容,它是Web 開發中常遇到的問題,其中提供了一些內置的模塊來幫你處理上傳上來的文件。它也會告訴你如何自定義後端存儲。
- Class-based views:這部分可以理解為更復雜的view 函數,只不過這裡介紹的是類。通過類可以提供更好的複用,從而避免自己寫很多代碼。當你發現你的view 中有太多業務代碼時,可以考慮參考這部分內容把代碼改造為class-based view(簡稱CBV)。如果你的代碼中有很多類似的view 函數,也可以考慮這麼做。這部分文檔就是告訴你,在Django中,如何更好地構建和複用View。
- Advanced:更高級的部分,告訴你如何把數據導出為CSV 或者PDF 格式。之所以冠名為“更高級”,可能是因為用得少。
- Middleware:中間件(中間層),無論怎麼翻譯,你得理解它的作用,這一部分代碼作用於WSGI(或者socket 連接)和View 之間。還記得第2 章講的WSGI 中間件的內容嗎,一樣的邏輯,都是對view 函數做了一個包裝(確切地說,是對View 的輸入request 和輸出response 進行處理),但是稍微複雜了一些。在Django 中,安全、session、整站緩存的內容都在這一塊了。
3. Template 部分
這是Django 聲稱對設計師友好的部分,並且確實如此。因為它提供的語法很簡單,即便你不懂編程,也可以很容易學習和使用。
- The basics:這部分介紹了Django 模板的基本配置以及基本的模板語法,還有如何使用其他模板引擎(比如Jinja2)的配置。
- For designers:這部分說是給設計師看的,但是你也應該看一看,裡面包含基礎的控制語句、註釋、內置的過濾器和標籤,還有最重要的針對用戶友好的數字的展示。
- For programmers:這個程序員更應該看看了,裡面包含如何將數據傳遞到模板中,如何配置模板以至於能夠在View 中更好地渲染模板,還有如何對現有模板所提供的簡單功能做更多定製。
4. Form 部分
無論是對於傳統的、需要通過Form來提交數據的頁面,還是通過Ajax 的方式提交數據到後端,Form 都是非常好用的,它可以幫我們比較優雅地處理和校驗來自外部的數據。它的工作原理跟ORM 很像(關於ORM是什麼,可以參見https://www.the5fire.com/what-is-orm.html)。Form是對HTML 中Form表單的抽象,可以方便我們用Python 代碼來直接操作頁面傳遞過來的數據。
- The basics:基礎的API 介紹,裡面有類似於Model 的Field 的部分,還有組件(widget)的部分。
- Advanced:更豐富的用法,包括如何把Form同Model 結合(Model 也有Field,Form也有Field,共用一套行不行?),以及如何把靜態資源渲染到頁面上。如果Form足夠好用,我們根本無須頻繁操作模板。還有如何佈局字段,一行展示一個還是一行展示多個。此外,還有更加詳細、深入的部分,那就是如何定義字段級別的驗證功能,比如頁面上只允許輸入數字的地方如何驗證。
這部分在開發admin 時很常用,因為admin 跟Model 結合緊密,如果需要去改模板的話,成本會有點高,所以更好的做法是通過自定義Form以及widget 來實現我們需要的功能。很多時候,我們不需要操作頁面就可以完成需求,這對後端程序員來說是相當友好的。
另外,即便是對於現在流行的單頁應用,也可以使用Form來處理接口拿到的數據,其邏輯跟傳統的提交表單的方式沒太多不同。
5. 開發流程
這一部分建議一定要看!很重要。如果說前面的內容都是為了讓你掌握使用Django 的技能,那麼這一階段就是讓你的代碼釋放能力。開發完成後,應該怎麼部署系統?有哪些東西是需要配置的?線上環境應該是什麼樣的?
你應該仔細看看這部分。
- Settings:不得不說,Django 的可配置項非常多,即便是工作了多年的人,也可能有一些配置完全沒用到過。但是,你知道有哪些配置,每個配置的作用是什麼嗎?舉個簡單的例子,你想讓系統出現異常時自動發送郵件告警,該怎麼做?自己寫一個發郵件的功能?想太多了,看看這些配置,你就知道了,只需要配置幾個參數就能達到目的。
- Applications:在Django 中,App 是一個很重要的概念,我們需要把業務拆分成不同的App來開發,那麼如何管理這些App,如何在程序運行時獲取到有哪些App,每個App 的信息是什麼,這裡可以瞭解到。
- Exceptions:異常是每個程序員(不論什麼水平)都無法避免的問題,唯一的差別在於,水平較高的人能夠輕鬆通過異常提示找到問題所在。這一部分列舉了Django 中常用異常的定義,在什麼情況下會出現異常。這部分只需瀏覽一遍即可,很多異常你在開發時會經常遇到,尤其是使用Django 的初期。
- django-admin and manage.py:這是每一個人接觸Django 時用到的第一個命令,這部分詳細介紹了django-admin 和manage.py 的使用方式,以及它們有哪些功能,比如最常用的runserver 以及migrate 等。另外,如果你需要自定義類似於runserver 這樣的命令時,可以參考這部分。
- Testing:單元測試是開發過程中的重要一環,它可以幫助你編寫更穩定的代碼,避免開發中的一些問題。在Django 中編寫TestCase 是相對容易的事,因為只需要根據它封裝好的結構來編寫自己的代碼就可以了。通過掌握很少的知識,就能完成單元測試的編寫。這一部分建議跟著寫一遍,會有收穫的。
- Deployment:代碼開發完了,最終怎麼上線,怎麼部署,怎麼給用戶提供服務呢?這些問題可以在這一部分得到解決。這部分詳細講解了如何通過WSGI 結合其他程序來部署你的應用,如何配置靜態文件,如何收集線上的異常。這一部分文檔需要你反覆查看,尤其是當你準備上線項目時,但要注意文檔只是用來參考的。
6. 其餘部分
Django 的文檔非常豐富,除了上面介紹的幾個比較大的主題外,還有很多小的主題也需要了解,這裡就不詳細羅列了,只是大概說說,等用到時,能夠知道到哪兒去看即可。
- The admin:如果你需要做基於內容的管理系統,這部分是逃不掉的。當然,你可以選擇不用它的admin,完全自己實現,但是相信我,掌握admin 能夠大大提高開發效率,沒有什麼比定義好一個Model,簡單配置後就能做出一個後臺管理系統更爽的事了。不過話說回來,Django 文檔上關於admin 部分的介紹沒有那麼多,但是從基本使用上來說是夠用的。更多的定製需要去參考源碼。
- Security:安全模塊,這部分也是一個成熟框架的標誌,一個成熟的框架一定經歷了種種安全的檢驗。這部分介紹了常見的Web 開發的安全問題,以及Django 是如何處理的。如果打算做Web 開發,安全問題不可忽視!
- Internationalization and localization:國際化和本地化(也稱i18n 和l10n)。如果你想要編寫國際化的軟件,詳細閱讀這部分必不可少。另外,Django 默認的語言是英語,時區是美國時間,因此在做admin 部分系統文案的展示時,需要修改默認語言為中文(zh-hans),如果要操作時間的話,需要把時區改為中國(Asia/Shanghai)。這裡需要注意的是,Django在寫數據庫時,日期和時間用的都是UTC 時間。因此,我們需要在這一部分了解如何使用Django 提供的函數處理時區轉換。
- Performance and optimization:性能和優化,它們也一樣很重要!對於程序員來說,精確知道你寫的代碼做了什麼事很重要。這部分算是Django 性能優化的索引。
- Geographic framework:這部分介紹瞭如何使用Django 開發GIS 系統,我在日常開發中沒有用到,不做過多說明。
- Common Web application tools:這部分你會經常用到,涉及用戶系統、權限系統、緩存、日誌、郵件發送、RSS、分頁、消息處理、序列化、session、sitemap、靜態文件管理和數據校驗等。隨用隨查即可,你需要知道的是Django 提供了這些功能。
- Other core functionalities:其他的一些核心功能,比如ETag 設置、內容類型和通用關係(如果你需要添加自定義的權限配置,或者想要了解Django 的權限邏輯時,需要了解這部分內容)、頁面管理App、頁面重定向模塊、信號模塊(這也很常用,尤其是當你打算監聽某個模型的數據操作時)、靜態檢測系統、site 模塊、Unicode 的使用等。隨著對Django用得越多,這些東西慢慢地都可以掌握。
- The Django open-source project:這部分包含如何參與到Django 社區中,以及Django的發佈流程、設計原則等一些社區文化類的文檔。
總結
Django 文檔已經相當棒了,非常完善,除了admin 部分寫得不是很深入。這就涉及另外一個話題,看文檔還是看代碼。關於admin 部分,如果有較多的需求,建議看完文檔後去摸索代碼。如果你熟悉了前面介紹的幾個層,那麼admin 代碼對你來說也不是什麼難事兒,甚至在某種情況下學得更快。
除了admin 部分,其他的文檔上基本都有。不過也沒有必要像背字典那樣去看文檔,有空瞜一眼,遇到問題瞜一眼。隨著實踐越來越多,對文檔會越來越熟悉,對它的依賴會越來越少。Django 的代碼結構跟文檔一樣劃分清晰,所以我現在大部分的問題都是靠讀代碼來解決,並且這是一個能夠不斷產生正向作用的方式,你越熟悉代碼,越有助於寫出更好的代碼,越能理解Django 的源碼思路。
最後需要提到的一點是,無論是Django 文檔還是其他文檔資料,都僅供參考。正確的答案始終是在你電腦上的代碼裡。不論是框架版本的差異,還是文檔上的書寫錯誤,都可能會捉弄你,而你電腦上的代碼始終不會騙你。
另外,可能有人會覺得Django 的文檔這麼多,這麼多功能什麼時候才能充分掌握呀!這個問題完全不用擔心,無論你選擇什麼框架,一開始接觸的都是簡單邏輯,隨著使用經驗的增長,你掌握的技能也越來越多,對Django 的瞭解也越來越多。
對於選擇學習Django 來說,它提供了很清晰的使用路線,你不需要像學習其他框架那樣,在入門之後還需要去網上搜羅各種資料才能明白接下來要幹什麼。
因此,選擇學習Django,是一件性價比很高的事情。接下來,我們通過一個簡單的例子來感受Django 的魅力。
學員管理系統的後臺開發
在上一節中,你瞭解了Django 所提供的功能,這一節就來切實體會一下,快速過一下Django的各個模塊。你最好打開熟悉的IDE(集成開發環境),一起寫起來。建議你在Linux 上開始這個練習。無論你是否喜歡在Linux 上做開發,掌握如何配置Linux 的開發環境和部署環境都是實際工作中無法繞過的門檻。
一、需求
我們先用一句話來描述需求:提供一個學員管理系統,其中有一個前臺頁面來展示現有學員,並供新學員提交申請,一個後臺負責處理申請。
接下來,我們逐步實現。
二、初始化環境
首先,創建虛擬環境:
mkvirtualenv student-env -p `which python3.6`
其中,最後的 -p 指明虛擬環境使用的是Python 3.6。不熟悉的可以看“使用virtualenv 創建虛擬Python 環境”,詳見https://www.the5fire.com/virtualenv-python-env.html。或者使用python3.6 -mvenv student-env 來創建虛擬環境。
然後激活虛擬環境:
workon student-env
如果是後面的創建方式,需要通過source student-env/bin/activate 來激活。接著,安裝Django,本書寫作時的最新版為1.11:
pip install django~=1.11
這裡解釋一下,~=表示安裝指定版本的最新版,這裡會去安裝Django 1.11.x 版本,x 表示1.11版本的最新版的小版本號。
三、創建項目
雖然可以不創建虛擬環境就安裝Django,但是我還是建議你在虛擬環境中安裝,因為在實際開發中,你可能需要維護不止一個項目。不同項目所依賴庫的版本也不同,如果你都安裝到root或者user 下,會出現衝突。
好了,切換到你喜歡的目錄中,比如 /home/the5fire/workspace/。然後,創建項目根目錄:mkdir student_house,這是我們的項目目錄。接著,再來創建項目:cd student_house && django-admin startproject student_sys(注意:要保證上面的虛擬環境是激活狀態)。
此時我們能得到下面的結構:
四、創建App
進入student_house/student_sys 中,通過上一步創建好的manage.py 創建一個App:python manage.py startapp student。現在目錄結構如下:
創建好之後,我們開始編寫代碼。
五、編寫代碼
我們可以在Model 層開始寫代碼了,這是一個簡單的需求,只需要一個Model 就可以滿足。在文件student_house/student_sys/student/models.py 中編寫如下代碼:()
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
class Student(models.Model):
SEX_ITEMS = [
(1, '男'),
(2, '女'),
(0, '未知'),
]
STATUS_ITEMS = [
(0, '申請'),
(1, '通過'),
(2, '拒絕'),
]
name = models.CharField(max_length=128, verbose_name="姓名")
sex = models.IntegerField(choices=SEX_ITEMS, verbose_name="性別")
profession = models.CharField(max_length=128, verbose_name="職業")
email = models.EmailField(verbose_name="Email")
qq = models.CharField(max_length=128, verbose_name="QQ")
phone = models.CharField(max_length=128, verbose_name="電話")
status = models.IntegerField(choices=STATUS_ITEMS, default=0, verbose_name=
"審核狀態")
created_time = models.DateTimeField(auto_now_add=True, editable=False,
verbose_name="創建時間")
def __str__(self):
return '<student>'.format(self.name)
class Meta:
verbose_name = verbose_name_plural = "學員信息"
/<student>
代碼說明:如果是基於Python 3.6 的代碼,可以去掉最上面的兩行coding: utf-8 以及from__future__ import unicode_literals。這裡加上這兩行,是為了兼容Python2.7。這意味著,如果你們公司目前整體是基於Python 2.7 的,那應該儘量在代碼上加上這兩行,方便以後進行升級。
再來寫admin.py:
from django.contrib import admin
from .models import Student
class StudentAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'sex', 'profession', 'email', 'qq', 'phone',
'status', 'created_time')
list_filter = ('sex', 'status', 'created_time')
search_fields = ('name', 'profession')
fieldsets = (
(None, {
'fields': (
'name',
('sex', 'profession'),
('email', 'qq', 'phone'),
'status',
)
}),
)
admin.site.register(Student, StudentAdmin)
寫完這兩個配置,Model 和admin 的界面就好了。接下來,我們把student 這個App 放到settings.py 中。
我們只需要在INSTALLED_APPS 配置的最後或者最前面增加'student'即可。
settings.py 文件如下:
INSTALLED_APPS = [
'student',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
好了,後臺部分完成了。接著,我們通過下面的命令創建表以及超級用戶。
- cd student_house/student_sys/:如果你已經在student_sys 目錄下,就不用切換目錄了。
- python manage.py makemigrations:創建數據庫遷移文件。
- python manage.py migrate:創建表。
- python manage.py createsuperuser:根據提示,輸入用戶名、郵箱和密碼。
此時通過 python manage.py runserver 命令啟動項目後,訪問 http://127.0.0.1:8000,會看到一個提示頁,這是因為我們還沒開發首頁。我們可以進入到admin 的頁面:http://127.0.0.1:8000/admin/。用你創建好的賬戶登錄,就能看到一個完整的帶有CURD 的後臺了。
六、基礎配置(中文)
通過上面的配置,你看到的界面應該是英文的,並且時區也是UTC 時區,所以需要進一步配置。
在settings 中做如下配置:
LANGUAGE_CODE = 'zh-hans' # 語言
TIME_ZONE = 'Asia/Shanghai' # 時區
USE_I18N = True # 語言
USE_L10N = True # 數據和時間格式
USE_TZ = True # 啟用時區
修改完這些之後,刷新一下試試。你可以嘗試修改上面的配置,看看分別對應什麼功能。
到這一部分,我們基本完成了admin 的部分。在下一節中,我們來完成頁面提交數據的部分,看下如何使用Form。
七、總結
對於簡單的後臺需求,通過admin 很容易滿足,這也是Django admin 被稱為“殺手鐧”的原因。
學員管理系統的前臺開發
有了上一節中Model 和admin 的部分,我們已經得到一個管理後臺了。接著,我們來做一個簡單的用戶提交申請的表單頁面。
一、開發首頁
首先,在student/views.py 文件中編寫下面的代碼:
from django.shortcuts import render
def index(request):
words = 'World!'
return render(request, 'index.html', context={'words': words})
我們定義了函數index,其參數是request,它是Django 對用戶發送過來的HTTP 請求的封裝。從request 對象上,我們能得到一些有用的信息。在函數最後,我們用Django 提供的一個快捷方法render 來渲染頁面,其中使用了模板文件index.html。我們需要在student 目錄下創建templates 文件夾,這個文件夾是Django 在渲染頁面時會默認查找的。
這部分需要多說幾句。Django 在渲染模板或者靜態頁面時,會去每個App 下查找,這裡說的App 就是settings.py 文件中配置的INSTALLED_APPS 中的App。Django 會去這些App 目錄下的templates 文件夾查找你在render 上用到的模板,並且是順序查找的(從上到下)。這意味著,如果你有兩個App,比如studentA 和studentB,若這兩個App 中都存在templates/index.html,那麼Django 會加載位置在前的那個App 的index.html 文件。靜態文件也是同樣的邏輯,你可以自行嘗試。通過這種方式,我們也可以覆蓋admin 的部分模板。不過這是基於Django 內置的規則,有些隱晦。如果可以通過其他方式解決,不建議使用這種路徑覆蓋的方式。
創建好templates/index.html 之後,我們開始編寫頁面代碼:
<title>學員管理系統-by the5fire/<title>
Hello {{ words }}!
這裡面只用到一個Django 的模板語法——{{ words }}。這裡需要注意,words 兩側的空格是依據“Django 編碼規範”(詳見https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/)來寫的。這個語法的意思就是從上下文中獲取到words 變量。這個變量是在index 中調用render 時傳遞過來的。
接著,再配置一下URL,也就是提供一個URL 映射,可以讓用戶訪問URL 時把數據發送到我們定義的index 這個View 上。
我們直接修改student_sys 目錄下的urls.py 文件:
from django.conf.urls import url
from django.contrib import admin
from student.views import index
urlpatterns = [
url(r'^$', index, name='index'),
url(r'^admin/', admin.site.urls),
]
然後通過python manage.py runserver 命令再次啟動項目,訪問http://127.0.0.1:8000,就能看到輸出的Hello World!!了。
二、輸出數據
接下來的工作就是把數據從數據庫表裡面取出來,渲染到頁面上。你可以先在admin 後臺創建幾條學員數據,以便於測試。
我們需要修改views.py 中的代碼:
from django.shortcuts import render
from .models import Student
def index(request):
students = Student.objects.all()
return render(request, 'index.html', context={'students': students})
先解釋一下上面代碼的意思:index 函數已經定義過了,也好理解,內部邏輯做了一些調整。首先,通過Student 模型拿到所有的student 數據,接著把數據放到context 裡面傳遞到模板中。
接著,修改index.html 中的代碼:
<title>學員管理系統-by the5fire/<title>
- {{ student.name }} - {{ student.get_status_display }}
{% for student in students %}
{% endfor %}
這樣我們就輸出了一個簡單的列表,展示了學員名稱和目前狀態。這裡有一個地方需要注意,那就是{{ student.get_status_display }}。在Model 中我們只定義了status 字段,並未定義這樣的字段,為什麼能通過這種方式取到數據呢?並且在admin 中,也沒有使用這樣的字段。
原因就是,對於設置了choices 的字段,Django 會幫我們提供一個方法(注意是方法),用來獲取這個字段對應的要展示的值。回頭看看status 的定義:
## 省略上下文代碼
STATUS_ITEMS = [
(0, '申請'),
(1, '通過'),
(2, '拒絕'),
]
status = models.IntegerField(choices=STATUS_ITEMS, verbose_name="審核狀態")
## 省略上下文代碼
在admin 中,展示帶有choices 屬性的字段時,Django 會自動幫我們調用get_status_display方法,所以不用配置。而在我們自己寫的模板中,這需要自己來寫。並且為了簡化模板的使用,默認只支持無參數的方法調用,你只需要寫方法名稱即可,後面的括號不能寫,Django 會自行幫你調用(如果是方法的話)。
三、提交數據
輸出數據之後,我們再來開發提交數據的功能,這部分我們用一下Form。
首先,創建一個forms.py 文件,它跟views.py 同級。編寫如下代碼:
from django import forms
from .models import Student
class StudentForm(forms.Form):
name = forms.CharField(label='姓名', max_length=128)
sex = forms.ChoiceField(label='性別', choices=Student.SEX_ITEMS)
profession = forms.CharField(label='職業', max_length=128)
email = forms.EmailField(label='郵箱', max_length=128)
qq = forms.CharField(label='QQ', max_length=128)
phone = forms.CharField(label='手機', max_length=128)
這個StudentForm 的定義是不是很熟悉?它跟Model 的定義類似,那麼我們能不能複用Model 的代碼呢?答案是:可以。還記得3.1 節文檔介紹的部分嗎?有一個ModelForm 可以用。
我們來改一下:
from django import forms
from .models import Student
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = (
'name', 'sex', 'profession',
'email', 'qq', 'phone'
)
只需要這麼改就行了,不需要重複定義N 多個字段。如果有修改對應字段類型的需求,比如把qq 改成IntegerField 來做數字校驗,也是可以聲明出來的。當然,我們也可以通過定義clean 方法的方式來做。我們來改一下代碼,增加QQ 號必須為純數字的校驗:
from django import forms
from .models import Student
class StudentForm(forms.ModelForm):
def clean_qq(self):
cleaned_data = self.cleaned_data['qq']
if not cleaned_data.isdigit():
raise forms.ValidationError('必須是數字!')
return int(cleaned_data)
class Meta:
model = Student
fields = (
'name', 'sex', 'profession',
'email', 'qq', 'phone'
)
其中clean_qq 就是Form 會自動調用來處理每個字段的方法。比如,在這個Form 中,你可以通過定義clean_phone 來處理電話號碼,可以定義clean_email 來處理郵箱等。如果驗證失敗,可以通過raise forms.ValidationError('必須是數字!')的方式返回錯誤信息,這個信息會存儲在Form中,最終會被我們渲染到頁面上。
有了Form,接下來需要做的就是在頁面中展示Form,讓用戶能夠填寫信息提交表單。同時對於提交的數據,我們需要先做校驗,通過後再將其保存到數據庫中。我們來看看views.py 最終的樣子:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from .forms import StudentForm
from .models import Student
def index(request):
students = Student.objects.all()
if request.method == 'POST':
form = StudentForm(request.POST)
if form.is_valid():
cleaned_data = form.cleaned_data
student = Student()
student.name = cleaned_data['name']
student.sex = cleaned_data['sex']
student.email = cleaned_data['email']
student.profession = cleaned_data['profession']
student.qq = cleaned_data['qq']
student.phone = cleaned_data['phone']
student.save()
return HttpResponseRedirect(reverse('index'))
else:
form = StudentForm()
context = {
'students': students,
'form': form,
}
return render(request, 'index.html', context=context)
裡面有一個form.cleaned_data 對象,它是Form 根據字段類型對用戶提交的數據做完轉換之後的結果。另外,還用了reverse 方法。我們在urls.py 中定義index 的時候,聲明瞭name='index',所以這裡可以通過reverse 來拿到對應的URL。這麼做的好處是,不需要硬編碼URL 到代碼中,這意味著如果以後有修改URL 的需求,只要index 的名稱不變,這個地方的代碼就不用改。
上面我們通過手動構建Student 對象的方法來保存Student 數據。其實在ModelForm中,因為有了Model 的定義,手動構建Student 對象的步驟可以省掉,我們可以將代碼修改如下:
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.shortcuts import render
from .forms import StudentForm
from .models import Student
def index(request):
students = Student.objects.all()
if request.method == 'POST':
form = StudentForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('index'))
else:
form = StudentForm()
context = {
'students': students,
'form': form,
}
return render(request, 'index.html', context=context)
對於HTTP GET 請求的數據,最後我們會把StudentForm 的實例傳到模板中,這樣用戶最終才能看到一個可以填寫數據的表單。要在模板中加form,是相當簡單的一件事。最終模板index.html 的代碼如下:
<title>學員管理系統-by the5fire/<title>
- {{ student.name }} - {{ student.get_status_display }}
{% for student in students %}
{% endfor %}
其中{% csrf_token %}是Django 對提交數據安全性做的校驗,這意味著,如果沒有這個token,提交過去的數據是無效的。這是用來防止跨站偽造請求攻擊的一個手段。在Web 開發中,如果有類似需求的話,這也是我們可以用來參考的技術點。
只需要這麼寫,Django 就會幫我們自動把所有字段列出來。當然,如果需要調整樣式,就要通過自己增加CSS 樣式文件解決了。
四、優化數據,獲取邏輯
在上述代碼中,我們在view 函數中通過students = Student.objects.all()的方式獲取到了所有學員的數據,但是這種方式在實際使用中會有問題。因為後期可能會對學員的數據進行過濾,比如需要調整為展示所有審核通過的學員,此時就需要調整view 函數中的代碼。或者對結果進行緩存,也需要調整代碼。
因此,可以把數據獲取邏輯封裝到Model 層中,對上暴露更語義化的接口。我們來稍微調整一下。
給models.py 中的Student 模型增加一個類方法:
# 省略其他代碼
class Student(models.Model):
# 省略其他代碼
@classmethod
def get_all(cls):
return cls.objects.all()
同時修改views.py 中的代碼:
# 省略其他代碼
def index(request):
students = Student.get_all()
# 省略其他代碼
這樣後期再修改需求時,只需要修改get_all 這個函數即可,改動的影響範圍就小了很多。
五、總結
到此為止,功能上已經完備。你可以再次通過命令python manage.py runserver 啟動項目,訪問http://localhost:8000,看看頁面上的功能是否可用。根據自己的想法調整代碼,反覆運行並查看效果,以便於理解Form的作用。
總結
這一部分中的三個(還有一個文中未提及)技能點有助於你一開始就建立好一個完整的模型,可以幫助你更好地理解Django。不過如果要更好地掌握這些內容,需要進一步實踐才行。你需要做的就是把本文的代碼完整敲一遍,然後根據自己的理解改改代碼,接著不斷地運行測試。
本文就到這裡,打開你熟悉的IDE,開始運行上面的代碼吧,會有很多收穫。
——本文節選自《Django企業開發實戰:高效Python Web框架指南》
資深開發工程師the5fire多年開發經驗總結。
以從零開發一個博客系統為例,介紹Django在日常工作中的應用。
本書共分為四部分。第一部分介紹編碼之前的準備工作,包括需求分析、Web開發基礎以及選型時Demo的練習。第二部分開始正式實現需求,介紹了環境配置、編碼規範以及合理的項目結構劃分。通過對Django各部分(Model、Form、admin、View)的介紹和使用,完成了一個基礎的博客系統。第三部分在前面的基礎上介紹Django第三方插件的使用,通過引入這些插件進一步增強我們的系統。最後一部分也是正式工作中必不可少的部分,包含調試代碼、優化系統、壓力測試以及自動化等內容。
——
你需要花費更多的時間來學習 Django,這是因為它提供的內容遠多於其他框架。剛開始,可能會覺得很多地方不明白,但是等你熟悉了之後就會發現,在 Web 應用開發上,Django 已經提供了很完備的支持,比如登錄認證、權限管理、admin 管理後臺、緩存系統、常見的 Web 安全防禦等。Django 每一層或者模塊所提供的功能都很清晰,比如什麼樣的需求在哪一層來處理,或者在哪個模塊中處理。
Django 一開始的學習曲線有點陡,然後是平緩上升的。先陡主要是因為新手需要一下子接受很多東西,但是隨著後面不斷使用和了解,你會發現,學習所耗費的時間完全值得。你可以更快地做出完善的系統,這會是一筆很划算的投資。
第一部分 初入江湖
第1章 需求
第2章 框架基礎和技術選型(到圖靈社區閱讀:http://www.ituring.com.cn/book/tupubarticle/25223)
第3章 Django小試牛刀
第二部分 正式開發
第4章 進入開發
第5章 奠定項目基石:Model
第6章 開發管理後臺
第7章 開發面向用戶的界面
第8章 引入前端樣式框架Bootstrap
第9章 完成整個博客系統
第三部分 第三方插件的使用
第10章 使用第三方插件增強管理後臺
第11章 使用django-rest-framework
第四部分 上線前的準備及線上問題排查
第12章 調試和優化
第13章 配置MySQL和緩存
第14章 上線前的準備
第15章 升級到Django 2.0
第16章 最後總結
附錄A 使用Fabric 2.0
附錄B 使用uWSGI來啟動Django程序
附錄C Sentry安裝和配置
附錄D 評論驗證碼功能
附錄E 通過signal來解耦代碼
附錄F 實現文章置頂的幾種方案
附錄G 以騰訊云為例演示部署流程
閱讀更多 圖靈教育 的文章