如何用Python Django創(chuàng)建網(wǎng)站?系列文章04(持續(xù)更新)
時(shí)間:2023-07-24 03:09:01 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)
時(shí)間:2023-07-24 03:09:01 來(lái)源:網(wǎng)站運(yùn)營(yíng)
如何用Python Django創(chuàng)建網(wǎng)站?系列文章04(持續(xù)更新):這個(gè)帖子會(huì)在之前積累的Ubuntu+Python+Django教程基礎(chǔ)上搭建 投票/問(wèn)卷調(diào)查 網(wǎng)站。
一、前期的準(zhǔn)備工作
- 需要準(zhǔn)備一臺(tái)PC或者筆記本,安裝上Ubuntu16.04版本,參考下面這個(gè)教程:
2. 需要租一臺(tái)阿里云服務(wù)器,選擇安裝Ubuntu16.04版本,參考第二個(gè)教程開始部分:
注意?? :經(jīng)常有同學(xué)一開始會(huì)問(wèn)說(shuō),能不能用windows系統(tǒng)或者macOS系統(tǒng)來(lái)對(duì)接阿里云的Ubuntu系統(tǒng)?
答案肯定是可以的,只是這個(gè)過(guò)程會(huì)遇到一些系統(tǒng)級(jí)別的bug。比如,為了把本地的django項(xiàng)目遷移部署到服務(wù)器,你需要在macOS系統(tǒng)生成django項(xiàng)目所需要的安裝包列表,其數(shù)量可能會(huì)遠(yuǎn)遠(yuǎn)多于你實(shí)際需要安裝的軟件包。所以,我們還是比較推薦用純命令窗口的 本地Ubuntu -- 阿里云Ubuntu 的遠(yuǎn)程連接模式。
二、搭建開發(fā)環(huán)境
1. 需要學(xué)習(xí)Python編程語(yǔ)言,選擇安裝PyCharm,參考系列文章01教程(在30行)如下:
Ubuntu16.04版本自帶Python2和Python3,而且我們已經(jīng)驗(yàn)證過(guò)了Python3.5是能夠滿足創(chuàng)建django網(wǎng)站項(xiàng)目需求的,所以,我們直接用系統(tǒng)自帶的Python3.5就可以了。
2. 需要學(xué)習(xí)Linux系統(tǒng)的架構(gòu)和常用命令,參考系列文章01教程(在50行)。
一開始學(xué)習(xí)一門新的語(yǔ)言會(huì)覺得內(nèi)容太多無(wú)從下手,怎么辦?初學(xué)者可以從最高頻的語(yǔ)句或者命令入手(限定知識(shí)的范圍來(lái)降低學(xué)習(xí)難度),配合上操作(編程是一種技能),逐漸擴(kuò)展到細(xì)分領(lǐng)域,效果就會(huì)很好。學(xué)習(xí)本質(zhì)上是一個(gè)逐漸建構(gòu)的過(guò)程
3. 安裝MySQL數(shù)據(jù)庫(kù),參考以下教程的第三點(diǎn):
如果安裝過(guò)程跳出下面這個(gè)界面,提醒你設(shè)置數(shù)據(jù)庫(kù)密碼,說(shuō)明你走在了正確的道路上:
4. 安裝python3-pip
·先更新,
apt-get update
·再安裝
apt-get install python3-pip
這里有可能出現(xiàn)權(quán)限問(wèn)題,在命令行前面加上sudo,以管理員權(quán)限來(lái)運(yùn)行更新和安裝,需要輸入一次系統(tǒng)密碼。從安裝python3-pip開始,之后的所有的軟件安裝,都是基于pip3來(lái)完成的。
5. 利用python3-pip來(lái)安裝虛擬環(huán)境virtualenv,具體參考的教程如下:
安裝虛擬環(huán)境的命令并不復(fù)雜:
sudo pip3 install virtualenv sudo pip3 install virtualenvwrapper
然而,啟動(dòng)虛擬環(huán)境卻讓人有點(diǎn)糾結(jié),存在兩種開啟模式:一種是
簡(jiǎn)便模式,也就是上面這個(gè)教程所采取的方法,這種方法的好處是創(chuàng)建虛擬環(huán)境的時(shí)候,文件夾里邊比較干凈沒有雜七雜八的內(nèi)容,進(jìn)入虛擬環(huán)境的狀態(tài)命令也比較簡(jiǎn)單,用workon XXX就可以啟動(dòng)虛擬環(huán)境。
另外一種是
原始模式,好處是不需要事先做涉及.bashrc文件的操作,缺點(diǎn)是文件夾里邊會(huì)有各種雜亂的內(nèi)容,啟動(dòng)虛擬環(huán)境的命令也會(huì)稍微有點(diǎn)麻煩,比如,source XXX/bin/activate。我自己在本地Ubuntu系統(tǒng)上workon成功了,但是,在阿里云服務(wù)器Ubuntu系統(tǒng)上沒有成功。所以,我現(xiàn)在比較推薦的方法是原始模式,這也比較符合我一直以來(lái)的“慫”的特點(diǎn),一旦有風(fēng)險(xiǎn)就選擇避開。
推薦創(chuàng)建和啟動(dòng)虛擬環(huán)境的命令如下:
·創(chuàng)建虛擬環(huán)境的語(yǔ)句:
virtualenv GP2
·激活虛擬環(huán)境的代碼:
source GP2/bin/activate
·退出的命令是:
deactivate
6. 指定版本安裝django
pip3 install django==1.11.7
7. 為了配合django項(xiàng)目的具體需求,我們還需要額外安裝一些軟件包,比如:
pip3 install PyMySQL
pip3 install pytz
·前者PyMySQL是一個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)模塊,要想將django與mysql數(shù)據(jù)庫(kù)連接,需要在django項(xiàng)目的初始化__init__.py文件中添加專門的語(yǔ)句:
import pymysqlpymysql.installasMySQLdb()
·后者pytz是專門為數(shù)據(jù)庫(kù)的表格添加時(shí)間信息有關(guān)的字段服務(wù)的。
Django開發(fā)者并沒有一開始就深思熟慮地考慮時(shí)區(qū)問(wèn)題,這個(gè)部分是在美國(guó)本土之外的開發(fā)者,也就是我們東方人,形成的全球化視角的優(yōu)勢(shì)之一?
三、開發(fā)民意調(diào)查/投票網(wǎng)站
這個(gè)教程所演示的投票網(wǎng)站主要是想實(shí)現(xiàn)以下兩個(gè)目標(biāo):
第一,后臺(tái)的網(wǎng)站管理功能部分,管理員可以添加、修改、和刪除調(diào)查問(wèn)卷。
第二,公開的網(wǎng)站投票網(wǎng)頁(yè)部分,實(shí)現(xiàn)的是用戶網(wǎng)上投票和瀏覽結(jié)果的功能。
1. 啟動(dòng)一個(gè)已經(jīng)創(chuàng)建好的虛擬環(huán)境GP2 打開已經(jīng)安裝了Ubuntu系統(tǒng)的筆記本電腦,點(diǎn)擊Terminal客戶端,按照常規(guī)的操作進(jìn)入到已經(jīng)創(chuàng)建好的GP2文件夾。
linux系統(tǒng)的常用命令有兩個(gè):ls相當(dāng)于眼睛 ,cd相當(dāng)于進(jìn)入
激活虛擬環(huán)境的命令,之前我們說(shuō)過(guò),我們選擇了保守模式
source GP2/bin/activate
2. 創(chuàng)建一個(gè)名叫myPolls的Django項(xiàng)目進(jìn)入到GP2文件夾中之后,我們要?jiǎng)?chuàng)建一個(gè)叫做myPolls的Django項(xiàng)目:
> django-admin startproject myPolls
這里的myPolls代表的是民min意yi調(diào)查問(wèn)卷polls的意思,my又有“我的”含義,一語(yǔ)雙關(guān)。
3. 在項(xiàng)目文件夾中創(chuàng)建一個(gè)app01_myPolls應(yīng)用進(jìn)入到myPolls文件夾,利用下面的命令創(chuàng)建一個(gè)名叫app01_myPolls的應(yīng)用:
python3 manage.py startapp app01_myPolls
利用tree命令可以查看整個(gè)文件夾的結(jié)構(gòu):
文件夾有兩個(gè),一個(gè)是項(xiàng)目同名的文件夾myPolls,里邊有(1)項(xiàng)目的啟動(dòng)文件__init__.py(之前說(shuō)的數(shù)據(jù)庫(kù)連接語(yǔ)句pymysql就是存放在這個(gè)位置);(2)項(xiàng)目配置文件settings.py,所有有關(guān)項(xiàng)目設(shè)置的參數(shù)都在這個(gè)文件里邊;(3)項(xiàng)目的路由urls.py文件是整個(gè)項(xiàng)目的入口,用戶通過(guò)url訪問(wèn)服務(wù)器,最先對(duì)接django項(xiàng)目的就是該文件;(4)最后一個(gè)是wsgi.py 文件,它是一個(gè)WSGI兼容的Web服務(wù)器的入口,用來(lái)運(yùn)行當(dāng)前的Django項(xiàng)目。
另外一個(gè)是app01_myPolls文件夾,里邊有(1)應(yīng)用后臺(tái)管理文件admin.py,(2)應(yīng)用配置文件apps.py,(3)應(yīng)用初始化文件__init__.py,(4)模型文件models.py和(5)視圖文件views.py等。
為了更好地理解Django項(xiàng)目MTV架構(gòu),我們來(lái)看一張圖:
用戶先是通過(guò)瀏覽器上的網(wǎng)址url進(jìn)入到Django項(xiàng)目中,最先對(duì)接的就是項(xiàng)目文件夾中的urls.py文件,然后進(jìn)入到應(yīng)用app01_myPolls的views.py文件,在這個(gè)文件中會(huì)經(jīng)歷兩重操作,一個(gè)是通過(guò)models來(lái)獲取數(shù)據(jù)庫(kù)中的數(shù)據(jù),另外一個(gè)是以templates來(lái)獲取網(wǎng)頁(yè)的模版,最后將兩者渲染render之后返回給用戶瀏覽。
從這個(gè)角度來(lái)看,MTV在整個(gè)Django項(xiàng)目中,M(models.py)有了,V(views.py)也有了,唯獨(dú)沒有T(templates)。這個(gè)模版部分的文件夾templates和其中的.html文件,都是需要我們?cè)赑yCharm中手動(dòng)添加和創(chuàng)建的。
4. 添加templates文件夾并修改與其對(duì)應(yīng)的setting.py中的內(nèi)容打開PyCharm開發(fā)者工具:
點(diǎn)擊打開myPolls項(xiàng)目文件夾,如下圖所示:
一開始項(xiàng)目進(jìn)來(lái),因?yàn)樽钕冗\(yùn)行的是myPolls項(xiàng)目文件夾中的urls.py文件,所以我故意雙擊點(diǎn)開這個(gè)文件,讓大家看到里邊的內(nèi)容。實(shí)際上,一開始打開項(xiàng)目文件夾時(shí),右側(cè)是空的,沒有顯示任何一個(gè)具體文件的內(nèi)容。
打開PyCharm開發(fā)者工具,最先要做的事情是添加templates模版文件夾。這一步具體實(shí)現(xiàn)的方法是,在
最左邊上面的myPolls項(xiàng)目根目錄文件夾的位置右鍵點(diǎn)擊,選擇新建文件夾Directory,這樣才能保證這個(gè)文件夾創(chuàng)建的位置與應(yīng)用app01_myPolls文件夾在同一級(jí)目錄中。同時(shí),特別需要強(qiáng)調(diào)的是,這個(gè)添加的過(guò)程,還要伴隨2個(gè)操作:
第一個(gè)操作是,在添加了文件夾之后,在PyCharm中要右鍵點(diǎn)擊文件夾templates,如下圖所示:
Mark Directory as --> Template Folder,
這個(gè)把templates文件夾標(biāo)記為模版文件夾“Template Folder”的過(guò)程,不會(huì)改變項(xiàng)目的任何代碼,但是,標(biāo)記了Template Folder會(huì)影響之后PyCharm的代碼提示功能。
第二個(gè)操作是,修改settings.py文件中55行左右的位置,在TEMPLATES中,找到 'DIRS': [],在這個(gè)中括號(hào)中添加一段代碼:
os.path.join(BASE_DIR,'templates'),
如下圖所示:
有了模版文件夾templates之后,我們就可以往里邊添加.html文件了(這里只是提一下,并沒有實(shí)際操作,后面會(huì)有具體的創(chuàng)建.html的操作)。
.html是什么?
html是超文本文件,文件里邊包含一些標(biāo)簽和數(shù)據(jù),代表著“網(wǎng)頁(yè)”的結(jié)構(gòu)和內(nèi)容。所謂的超文本,就是在文本的基礎(chǔ)上,還包含有聲音,圖片和視頻等多媒體文件。那么這些多媒體文件放在哪里呢?
答案是static。
實(shí)際上,我們看到的“網(wǎng)頁(yè)”一般都是由三個(gè)部分構(gòu)成:html5 + css3 + javascript,css決定了“網(wǎng)頁(yè)”的樣式風(fēng)格,js決定的是具體的動(dòng)作。我們把.html放在templates文件夾里邊,那么css文件和js文件放哪里呢?
答案仍然是static。
5. 添加static文件夾并修改與其對(duì)應(yīng)的setting.py中的內(nèi)容在PyCharm開發(fā)者工具
最左邊上面的myPolls項(xiàng)目根目錄文件夾的位置鼠標(biāo)右鍵點(diǎn)擊,選擇新建文件夾Directory,將其命名成static。
然后,在settings.py文件最后一行的后面添加代碼:
STATIC_URL = '/static/' #最后一行
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
簡(jiǎn)單總結(jié)第4和第5步,"添加templates和static文件夾,并在配置文件中添加它們的路徑"。
6. 設(shè)置項(xiàng)目的解釋器在第1步,我們啟動(dòng)一個(gè)虛擬環(huán)境GP2,采用的是Python3.6版本。但是,這些在命令窗口Terminal中完成的操作,PyCharm是不能自動(dòng)識(shí)別的。因此, 當(dāng)我們進(jìn)入到PyCharm環(huán)境中時(shí),切記要在File菜單的Settings選項(xiàng)里設(shè)定整個(gè)項(xiàng)目的解釋器,指向哪個(gè)虛擬環(huán)境,用哪個(gè)Python版本。
在Settings里邊有兩個(gè)選項(xiàng),Project Structure 和 Python Interpreter,我們選擇Python Interpreter,然后在下拉菜單中選擇帶有Show All選項(xiàng),如下圖所示:
點(diǎn)擊之后,選擇其中的Python 3.6(GP2)選項(xiàng),見下圖:
然后我們會(huì)看到:
點(diǎn)擊Apply按鈕,再點(diǎn)擊OK,項(xiàng)目解釋器設(shè)置完畢。
7.
注冊(cè)應(yīng)用app01_myPolls我們?cè)趧?chuàng)建myPolls項(xiàng)目之后,緊跟著創(chuàng)建了一個(gè)app01_myPolls應(yīng)用,這個(gè)應(yīng)用myPolls項(xiàng)目是不認(rèn)的。為什么不認(rèn)?因?yàn)檫@個(gè)應(yīng)用并沒有注冊(cè)!
之所以沒有注冊(cè),是因?yàn)閼?yīng)用本身是獨(dú)立于項(xiàng)目存在的,這個(gè)可能是Django開發(fā)者一開始就設(shè)定好的,這樣做的好處是方便把某個(gè)應(yīng)用從一個(gè)項(xiàng)目遷移到另外一個(gè)項(xiàng)目??紤]到高內(nèi)聚,低耦合,可擴(kuò)展,和可維護(hù)是一個(gè)優(yōu)秀程序員的金標(biāo)準(zhǔn)。應(yīng)用和項(xiàng)目解耦合的結(jié)果是,在用命令創(chuàng)建應(yīng)用app時(shí),它是獨(dú)立進(jìn)行的,不會(huì)自動(dòng)地在項(xiàng)目中注冊(cè)。如果你要把創(chuàng)建的應(yīng)用納入到項(xiàng)目中,就需要手動(dòng)注冊(cè)一下。怎么才能手動(dòng)注冊(cè)呢?
我們要打開myPolls項(xiàng)目的同名文件夾myPolls下的settings.py文件,把a(bǔ)pp01_myPolls應(yīng)用寫入到INSTALLED_APPS里邊:
8.
修改配置setting.py文件中的一些項(xiàng)目(host, code, & timezone)除了添加了templates和static路徑信息,注冊(cè)了應(yīng)用之外,還剩下一些配置參數(shù)需要修改:
(1)ALLOWED_HOSTS = [],修改成ALLOWED_HOSTS = [“*”]。
(2)LANGUAGE_CODE = 'en-us',修改成LANGUAGE_CODE = 'zh-hans'。
(3)TIME_ZONE = 'UTC',修改成TIME_ZONE = 'Asia/Shanghai'。
(4)USE_TZ = True,修改成USE_TZ = False。
配置文件中的這些項(xiàng)目修改好之后,就剩下最后一個(gè)信息,數(shù)據(jù)庫(kù)配置信息。
9. 配置數(shù)據(jù)庫(kù)db_myPolls注意MTV那張流程圖中,最底層是數(shù)據(jù)庫(kù)Database。要把PyCharm和MySQL數(shù)據(jù)庫(kù)連接起來(lái),這就需要配置兩個(gè)地方:一個(gè)是初始化的時(shí)候,需要載入pymysql驅(qū)動(dòng);另外一個(gè)是在配置文件settings.py中的80+行,需要修改數(shù)據(jù)庫(kù)相關(guān)的信息。
A. 修改項(xiàng)目初始化文件
點(diǎn)擊myPolls項(xiàng)目文件夾中的__init__.py文件,輸入下面這段代碼:
import pymysqlpymysql.install_as_MySQLdb()
B. 修改配置文件中的數(shù)據(jù)庫(kù)相關(guān)信息
點(diǎn)擊myPolls項(xiàng)目文件夾中的settings.py文件,將下面這段代碼:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }}
替換成:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'db_myPolls', 'USER': 'root', 'PASSWORD': 'Psycho0810', 'HOST':'localhost', 'PORT':'3306', }}
按照習(xí)慣,我會(huì)把數(shù)據(jù)庫(kù)的名字命名成db_項(xiàng)目名稱,也就是這里的db_myPolls。我們要在mysql中把這個(gè)叫做db_myPolls的數(shù)據(jù)庫(kù)創(chuàng)建出來(lái)。按理說(shuō),這個(gè)操作是要從PyCharm開發(fā)者工具切換到Terminal終端命令行窗口來(lái)完成。PyCharm已經(jīng)集成了命令窗口,所以這個(gè)時(shí)候,只需要點(diǎn)擊整個(gè)PyCharm界面的左下角位置三個(gè)選項(xiàng):“TODO”,“Python Console”,和“Terminal”,其中的Terminal選項(xiàng),就可以打開終端。
C. 如何在mysql中創(chuàng)建db_myPolls?
在命令行中輸入
mysql -uroot -p
它會(huì)要求輸入密碼,把之前設(shè)定好的密碼輸入敲回車,就可以進(jìn)入到mysql環(huán)境中。
在mysql環(huán)境下建立一個(gè)新的數(shù)據(jù)庫(kù)db_myPolls的代碼如下:
mysql> create database db_myPolls charset=utf8;
這里要注意別忘記寫charset=utf8這句話,因?yàn)?,如果沒有這句話,你沒有辦法導(dǎo)入中文。
這個(gè)時(shí)候可以調(diào)用下面這句命令看看mysql里邊都有哪些數(shù)據(jù)庫(kù):
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| db_mBlog |
| db_psyBlog |
| db_myPolls |
| mysql |
| performance_schema |
| sys |
+--------------------+
7 rows in set (0.01 sec)
D. 手動(dòng)連接myPolls項(xiàng)目與db_myPolls數(shù)據(jù)庫(kù)
點(diǎn)擊右側(cè)豎著的那兩個(gè)選項(xiàng)中的"Database",然后點(diǎn)擊“+”號(hào)-->選擇Data Sources這個(gè)選項(xiàng)中的MySQL,如下圖所示:
這個(gè)時(shí)候會(huì)彈出一個(gè)界面:
小心?。∵@是個(gè)坑,因?yàn)橐话闳诉M(jìn)入這個(gè)界面,想當(dāng)然地會(huì)認(rèn)為是從第一個(gè)編輯框Name開始填寫,錯(cuò)了?。?!你只需要填寫User + Password + Database就可以,它會(huì)自動(dòng)把信息合并了顯示在Name對(duì)著的編輯框中,如下圖所示:
這時(shí)候你點(diǎn)擊一下“Test Connection”按鈕,它如果顯示打勾,說(shuō)明連接數(shù)據(jù)庫(kù)通過(guò)了測(cè)試。點(diǎn)擊Apply應(yīng)用當(dāng)前設(shè)置,再點(diǎn)擊OK,PyCharm與MySQL數(shù)據(jù)庫(kù)的連接就建立成功了。
小結(jié):在虛擬環(huán)境中,創(chuàng)建Django項(xiàng)目和應(yīng)用之后,通過(guò)添加文件夾,修改配置文件,設(shè)定解釋器,和連接數(shù)據(jù)庫(kù)等一系列前期的操作,我們的PyCharm進(jìn)入到了整裝待發(fā)的狀態(tài)。
10. 小試牛刀:制作一個(gè)Hello World一般整個(gè)項(xiàng)目的框架搭建完后,都是會(huì)拿一個(gè)最簡(jiǎn)單的“Hello, World!”試著運(yùn)行一次,向互聯(lián)網(wǎng)世界宣告“我來(lái)啦!”,以及測(cè)試一下Django環(huán)境是不是搭建好了。
用戶通過(guò)在瀏覽器中輸入網(wǎng)址url訪問(wèn)服務(wù)器應(yīng)用,這個(gè)url請(qǐng)求在Django項(xiàng)目中是發(fā)給了項(xiàng)目文件夾myPolls中的urls.py來(lái)處理的,我們來(lái)截個(gè)圖:
這個(gè)urls.py是在整個(gè)項(xiàng)目下的,為了方便管理urls,Django發(fā)展出一套方法,可以在項(xiàng)目下的urls.py中設(shè)置include,將項(xiàng)目下的urls.py引導(dǎo)到應(yīng)用下的urls.py。可是,應(yīng)用下沒有urls.py啊?我們手動(dòng)建一個(gè)。在應(yīng)用文件夾app01_myPolls中新建一個(gè)urls.py文件,輸入:
urlpatterns = [
url(r'^hello', views.hello, name='hello'),
]
這句話的意思是,如果你在應(yīng)用網(wǎng)址后添加hello,它就會(huì)觸發(fā)視圖views.py文件中的hello回調(diào)函數(shù)。
重新切回到項(xiàng)目下的urls.py文件,輸入:
urlpatterns = [
url('admin/', admin.site.urls),
url(r'^app01_myPolls/', include('app01_myPolls.urls', namespace='app01_myPolls')),
]
這句話的意思是,如果你在網(wǎng)站的ip地址或者域名后面添加應(yīng)用app01_myPolls,它會(huì)自動(dòng)導(dǎo)向應(yīng)用app01_myPolls文件夾中的urls.py文件。
注意??:這個(gè)時(shí)候的include下面有紅色波浪線,說(shuō)明include這個(gè)方法所在的模塊還沒有導(dǎo)入進(jìn)來(lái)。這個(gè)時(shí)候就要進(jìn)行一次操作 "alt + enter",它會(huì)自動(dòng)提示你是不是要從django.conf.urls中載入include方法,用鼠標(biāo)點(diǎn)擊或者敲回車即可完成操作。這個(gè)地方專門截個(gè)圖,感受一下PyCharm作為Python+Django編程神器的威力:
神操作(alt + enter)完成之后,原來(lái)上面那句話就會(huì)在url后面增加include:
from django.conf.urls import url, include
原來(lái)include下面的紅色波浪線提示就沒了。
這個(gè)神操作也可以運(yùn)用到應(yīng)用app01_myPolls下的urls.py,url,views下面都有紅色波浪線提示,操作過(guò)載入相應(yīng)的模塊之后,紅色波浪線就消失了。再截圖一起感受一下:
注意文件上方的兩行代碼,它們是后來(lái)新添加的:
from django.conf.urls import urlfrom app01_myPolls import views
最后還剩下hello這個(gè)函數(shù),這個(gè)函數(shù)是指向views.py文件的,但是在views.py文件中這個(gè)函數(shù)是不存在的。同樣把鼠標(biāo)放在這個(gè)單詞上,同時(shí)按下"alt + enter"就可以看到:
敲回車,就會(huì)瞬間切到views.py文件中,
這個(gè)函數(shù)的代碼是自動(dòng)添加上去的,
def hello(request): return none
這個(gè)函數(shù)中,回調(diào)函數(shù)返回值是none,需要修改成
HttpResponse("Hello World!")
這個(gè)HttpResponse下面也是有紅色波浪線的,需要神操作載入相應(yīng)的模塊。
自此,第一個(gè)hello world網(wǎng)頁(yè)就做好了。我們可以在PyCharm下面的命令窗口中輸入
python3 manage.py runserver
在谷歌瀏覽器的網(wǎng)址輸入欄中敲入:
127.0.0.1:8000/app01_myPolls/hello
敲回車,就會(huì)看到下面這個(gè)畫面:
說(shuō)明之前所有的操作都是有價(jià)值的。我們可以基于現(xiàn)在這個(gè)Django框架,建構(gòu)出我們想要的問(wèn)卷調(diào)查網(wǎng)站。
11. 第一個(gè)目標(biāo):實(shí)現(xiàn)對(duì)后臺(tái)網(wǎng)站的管理功能后臺(tái)網(wǎng)站管理分為兩個(gè)部分,第一是問(wèn)卷的數(shù)據(jù),包括題目Question和選項(xiàng)Choice;第二是網(wǎng)站的用戶,分為普通用戶和超級(jí)管理員。
我們先來(lái)實(shí)現(xiàn)Question和Choice兩個(gè)模型的設(shè)計(jì)和構(gòu)建:
數(shù)據(jù)庫(kù)db_myPolls包含問(wèn)題Questions和選項(xiàng)Choices兩張表格,其中問(wèn)題表格是主表,選項(xiàng)表格是子表。以主表Questions的question_id字段作為外鍵關(guān)聯(lián)一張子表Choices。
外鍵(Foreign Key)是數(shù)據(jù)庫(kù)的一個(gè)很重要的概念。當(dāng)兩張表存在關(guān)聯(lián)字段的時(shí)候,利用外鍵可以保證主表和從表的一致性和完整性。
定義Questions和Choices兩個(gè)模型的代碼如下圖所示:
在線問(wèn)卷調(diào)查系統(tǒng)包括兩個(gè)模型/表格:?jiǎn)栴}(Questions)和選項(xiàng)(Choices)。問(wèn)題Questions模型包含兩個(gè)字段,分別是問(wèn)卷內(nèi)容(question_text)和提交時(shí)間(pub_date),同時(shí)問(wèn)題(Questions)模型包含一個(gè)叫做was_published_recently()的方法,用于判斷問(wèn)卷是不是最近(一天內(nèi))發(fā)布的;Choice同樣包含兩個(gè)字段選項(xiàng)內(nèi)容(choice_text)和選項(xiàng)得分(votes)。這里要注意的是,一旦對(duì)表格中的字段進(jìn)行了關(guān)聯(lián)外鍵的操作,也就意味著每一個(gè)選項(xiàng)都必然屬于一個(gè)問(wèn)卷,即構(gòu)成了所謂的多對(duì)一的關(guān)系。
文件app01_myPolls/models.py中的代碼分享一下:
#!/usr/bin/python# -*- coding: UTF-8 -*-from django.db import modelsfrom django.utils import timezoneimport datetimeclass Questions(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1) def __str__(self): return self.question_textclass Choices(models.Model): question = models.ForeignKey(Questions, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) def __str__(self): return self.choice_text
上面代碼中每一個(gè)類就是一個(gè)Django模型,它們都繼承自ddjango.db.models.Model類,而模型的每一個(gè)屬性都是Field類的實(shí)例。模型對(duì)應(yīng)數(shù)據(jù)庫(kù)表格,每個(gè)模型的屬性變量對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù)表格的字段。
為了讓模型文件models.py中的代碼生效,我們需要進(jìn)行兩步十分關(guān)鍵的操作,一步是生成遷移文件makemigrations,另外一步是遷移migrate。切換到命令窗口,輸入兩次命令并運(yùn)行:
python3 manage.py makemigrations
python3 manage.py migrate
先對(duì)整個(gè)PyCharm窗口進(jìn)行截圖,方便大家對(duì)遷移命令的運(yùn)行有一個(gè)直觀的印象:
再分享命令窗口的信息:
(GP2) psyjt@psyjt-ThinkPad-X1-Carbon-2nd:~/Projects/Project_Django/GP2/myPolls$ python3 manage.py makemigrationsMigrations for 'app01_myPolls': app01_myPolls/migrations/0001_initial.py - Create model Choices - Create model Questions - Add field question to choices(GP2) psyjt@psyjt-ThinkPad-X1-Carbon-2nd:~/Projects/Project_Django/GP2/myPolls$ python3 manage.py migrateOperations to perform: Apply all migrations: admin, app01_myPolls, auth, contenttypes, sessionsRunning 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 app01_myPolls.0001_initial... 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 sessions.0001_initial... OK(GP2) psyjt@psyjt-ThinkPad-X1-Carbon-2nd:~/Projects/Project_Django/GP2/myPolls$
所有這些操作,都會(huì)作用在數(shù)據(jù)庫(kù)上。我們可以通過(guò)打開PyCharm右側(cè)的Database,看看是不是除了Django自帶的一系列數(shù)據(jù)庫(kù)表格,還有我們新創(chuàng)建的Questions和Choices表格?
問(wèn)題和選項(xiàng)表格都在里邊,創(chuàng)建成功!歐耶?。?br>
出于好奇心,我們把問(wèn)題Questions和選項(xiàng)Choices兩個(gè)表格打開看下。
·先看問(wèn)題表格:
問(wèn)題Questions表格有三個(gè)字段,除了定義好的question_text和pub_date以外,還有一個(gè)自動(dòng)添加的id字段,而且很明顯它是主鍵primary key(簡(jiǎn)稱pk)。由此我們可以看出,模型定義的時(shí)候,它會(huì)自動(dòng)添加id主鍵。
·再看選項(xiàng)表格:
選項(xiàng)Choices表格有四個(gè)字段,除了事先定義的choice_text,votes和關(guān)聯(lián)的外鍵question_id以外,同樣會(huì)有一個(gè)自動(dòng)添加的id主鍵。
看到這里,大家已經(jīng)想到了,定義模型只是相當(dāng)于給整個(gè)Django項(xiàng)目的底層數(shù)據(jù)庫(kù)以表格的形式搭建了一個(gè)框架,但是,表格里邊是空的,沒有內(nèi)容,需要某個(gè)角色通過(guò)某種方式把內(nèi)容添加進(jìn)去。
我是超級(jí)管理員什么角色?
當(dāng)然是我們整個(gè)網(wǎng)站的超級(jí)管理員同志。
超級(jí)管理員說(shuō):“我是誰(shuí)?”,“我從哪里來(lái)?”,“我能為這個(gè)項(xiàng)目做什么?”
·我是誰(shuí)?
超級(jí)管理員是整個(gè)網(wǎng)站后臺(tái)管理權(quán)限最高的用戶,我們會(huì)給它一個(gè)酷炫的名字,叫做SuperUser。
·我從哪里來(lái)?
超級(jí)管理員是通過(guò)Python3+Django項(xiàng)目根目錄中的manage.py文件中自帶的命令createsuperuser來(lái)創(chuàng)建的。
python3 manage.py createsuperuser
·我能為這個(gè)項(xiàng)目做什么?
超級(jí)管理員可以實(shí)現(xiàn)對(duì)用戶的管理,而且它還能添加、修改和刪除數(shù)據(jù)庫(kù)各個(gè)表格中的內(nèi)容。具體的實(shí)現(xiàn),需要用到admin.site.register命令,打開app01_myPolls文件夾中的admin.py文件,通過(guò)輸入最后一行內(nèi)容配合上神操作(alt + enter)添加代碼如下:
from django.contrib import adminfrom .models import Questions, Choicesadmin.site.register(Questions)admin.site.register(Choices)
有了這兩句,超級(jí)管理員登錄之后,不僅可以管理用戶,也可以管理問(wèn)題Questions和選項(xiàng)Choices表格中的數(shù)據(jù)內(nèi)容。我們來(lái)看看登錄界面:
還有登錄進(jìn)去之后的管理界面:
這個(gè)界面就允許超級(jí)管理員手動(dòng)添加問(wèn)題和選項(xiàng)表格中的具體內(nèi)容。我們先來(lái)添加3個(gè)問(wèn)題,然后再給這三個(gè)問(wèn)題添加選項(xiàng)。添加問(wèn)題的界面截圖如下:
添加選項(xiàng)的界面如下圖所示:
省略中間手動(dòng)操作的過(guò)程,等以后我們變厲害了,這個(gè)手動(dòng)的過(guò)程是可以用run代碼來(lái)實(shí)現(xiàn)(這個(gè)挑戰(zhàn)性的任務(wù),已經(jīng)由我們實(shí)驗(yàn)室一位叫做ZYX的大佬搞定了,用了數(shù)據(jù)庫(kù)SQL語(yǔ)言;而且我們?cè)诰W(wǎng)上也找到了更簡(jiǎn)便的方法,不需要調(diào)用SQL,只需要Python語(yǔ)言也能夠搞定)。我們?cè)诳紤]用戶將題庫(kù)制作成excel表格,網(wǎng)站只需要提供一個(gè)接口,用戶點(diǎn)擊按鈕上傳本地excel文件,就可以自動(dòng)化導(dǎo)入到數(shù)據(jù)庫(kù)中的方法。
手動(dòng)添加數(shù)據(jù)成功之后,我們通過(guò)PyCharm中數(shù)據(jù)庫(kù)可視化工具來(lái)查看添加之后兩個(gè)表格中的內(nèi)容,先看問(wèn)題Questions表格中新添加的內(nèi)容:
再來(lái)看選項(xiàng)Choices表格里邊的數(shù)據(jù):
到目前為止,我們的投票/民意調(diào)查問(wèn)卷系統(tǒng)已經(jīng)有了管理員賬號(hào),有了網(wǎng)站后臺(tái)的管理系統(tǒng)(Django自帶的),并且我們還運(yùn)行了網(wǎng)站后臺(tái),用超級(jí)管理員登錄并在表格中添加了數(shù)據(jù)內(nèi)容。但是,整個(gè)網(wǎng)站還缺少公開的網(wǎng)站首頁(yè),投票網(wǎng)頁(yè)和投票結(jié)果展示網(wǎng)頁(yè)。
12. 第二個(gè)目標(biāo):實(shí)現(xiàn)用戶網(wǎng)上投票和瀏覽結(jié)果的功能在Django項(xiàng)目中,每一個(gè)網(wǎng)頁(yè)或者其他內(nèi)容都是通過(guò)視圖呈現(xiàn)出來(lái)的,每一個(gè)視圖就是一個(gè)Python函數(shù)或者方法(根據(jù)需要有時(shí)需要額外增加一個(gè)Python函數(shù)或者方法,并通過(guò)重定向指向另外一個(gè)視圖函數(shù))??傮w上來(lái)說(shuō),Django是通過(guò)url確定調(diào)用哪個(gè)視圖函數(shù),而它們又都取決于一個(gè)整體的網(wǎng)站設(shè)計(jì)構(gòu)想,如下圖所示:
我們要實(shí)現(xiàn)的前端網(wǎng)頁(yè)的構(gòu)想,主頁(yè)index、投票details和結(jié)果results三個(gè)視圖,以及處理投票的vote函數(shù),都是圍繞整個(gè)投票/民意調(diào)查系統(tǒng)的設(shè)計(jì)框架展開的。
整個(gè)問(wèn)卷系統(tǒng)的入口是項(xiàng)目myPolls文件夾下的urls.py文件,
進(jìn)而借出include方法觸發(fā)應(yīng)用app01_myPolls文件夾下的urls.py。在該應(yīng)用路由文件中,針對(duì)3個(gè)視圖,包括index,details,和results,還有一個(gè)vote處理函數(shù),我們要在應(yīng)用路由urls.py文件中添加四個(gè)url映射,如下圖所示:
小貼士:為超鏈接添加命名空間
這里專門在urls中植入了 命名空間,
namespace='app01_myPolls'
和類似這樣的,
name='index'
命名空間第一是可以有效地對(duì)變量進(jìn)行隔離,防止名稱相同的變量之間調(diào)用混亂的問(wèn)題;第二是可以配合反向解析reverse,在重定向的時(shí)候,有時(shí)候用reverse比較便利;第三是在模板中采用命名空間的形式來(lái)確定超鏈接,是Django項(xiàng)目比較常規(guī)的操作。
這里再需要補(bǔ)充一個(gè)url路由知識(shí),就是,如果需要從url中獲取一個(gè)值,需要對(duì)正則表達(dá)添加小括號(hào)。而且,一旦在url正則匹配中添加了小括號(hào)()來(lái)獲取參數(shù),在請(qǐng)求調(diào)用的函數(shù)中必須有接收的形參。以投票網(wǎng)頁(yè)details為例子,url是這么寫的:
url(r'^details/(/d+)$', views.details, name='details'),
那么,視圖views.py文件中的details函數(shù)就需要有專門的形參來(lái)獲取傳入的參數(shù)。
def details(request, question_id): return None
在視圖views.py文件中代碼,截圖如下:
小括號(hào)中的(/d+)代表的數(shù)字就傳給了question_id這個(gè)參數(shù)。完整版本的views.py文件中的代碼分享如下:
from django.http import HttpResponse, Http404, HttpResponseRedirectfrom django.shortcuts import render, get_object_or_404from django.urls import reversefrom app01_myPolls.models import Questions, Choices# Create your views here.def hello(request): return HttpResponse("Hello World!")def index(request): all_questionslist = Questions.objects.all() context = {'all_questionslist': all_questionslist} return render(request, "index.html", context)def details(request, question_id): try: question = Questions.objects.get(pk=question_id) context = {'question': question} except Questions.DoesNotExist: raise Http404("問(wèn)卷不存在") # return redirect(reverse("appPolls:index")) return render(request, "details.html", context)def results(request, question_id): question = get_object_or_404(Questions, pk=question_id) context = {'question': question} return render(request, 'results.html', context)def vote(request, question_id): question = get_object_or_404(Questions, pk=question_id) try: selected_choice = question.choices_set.get(pk=request.POST['choice']) selected_choice.votes = selected_choice.votes + 1 selected_choice.save() except(KeyError, Choices.DoesNotExist): return render(request, "details.html", {'question': question, 'error_message': "還沒有選擇任何項(xiàng)目"}) return HttpResponseRedirect(reverse('app01_myPolls:results', args=(question.id,)))
每一個(gè)視圖函數(shù)都負(fù)責(zé)一個(gè)具體的業(yè)務(wù)邏輯,視圖執(zhí)行結(jié)束之后會(huì)返回一個(gè)包含頁(yè)面內(nèi)容的HttpResponse對(duì)象或者異常信息。
什么是異常信息?
我們有時(shí)候訪問(wèn)網(wǎng)頁(yè)失敗,會(huì)跳出一個(gè)404錯(cuò)誤,就是典型的出現(xiàn)異常情況后瀏覽器呈現(xiàn)給用戶的反饋信息。404錯(cuò)誤是一個(gè)比較常見的網(wǎng)頁(yè)訪問(wèn)的錯(cuò)誤,當(dāng)被訪問(wèn)的url資源不存在的時(shí)候,瀏覽器就會(huì)拋出這類錯(cuò)誤。例如,上面的details視圖函數(shù),
try: question = Questions.objects.get(pk=question_id) context = {'question': question} except Questions.DoesNotExist: raise Http404("問(wèn)卷不存在") # return redirect(reverse("appPolls:index")) return render(request, "details.html", context)
如果這個(gè)時(shí)候,我們故意在瀏覽器中輸入
http://127.0.0.1:8000/app01_myPolls/1000
程序就會(huì)拋出異常,如下圖所示:
這當(dāng)然是我故意把url修改了導(dǎo)致的,一般情況下是不會(huì)出現(xiàn)這種情況的。由于404錯(cuò)誤是一個(gè)非常常見的網(wǎng)頁(yè)異常,所以Django也提供了一個(gè)簡(jiǎn)寫方法:get_object_or_404。所以在details視圖函數(shù)中我們使用了get_object_or_404()。
解釋完異常情況處理之后,我們先來(lái)看index視圖函數(shù)的功能是返回所有Questions表格中能夠查找到的調(diào)查問(wèn)卷。代碼中的render就是渲染函數(shù),它有三個(gè)參數(shù),第一個(gè)request是請(qǐng)求變量,第二個(gè)是模板.html文件名,第三個(gè)是傳入?yún)?shù)上下文變量context,一個(gè)類字典變量。當(dāng)引用render函數(shù)之后,代碼中就不需要httpResponse函數(shù)了,可以理解為render是HttpResponse的一種更為簡(jiǎn)潔的更高級(jí)的版本,它會(huì)先載入模板,然后基于傳入的參數(shù)context中的key-value對(duì)應(yīng)關(guān)系,將事先埋藏在模板中的塊和變量替換為具體的值(渲染應(yīng)該是這樣一個(gè)替換過(guò)程的統(tǒng)稱),最終返回給用戶(的瀏覽器)。
第一個(gè)視圖函數(shù)是index,可是它的模板文件“index.html“還沒有創(chuàng)建,需要在templates文件夾中創(chuàng)建。該文件里的h5的代碼分享如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>問(wèn)卷調(diào)查網(wǎng)站首頁(yè)</title> {% load static %} <link rel="stylesheet" type='text/css' href="{% static 'style.css' %}" /></head><body><div id="container"> <br><h2> 感謝您參與我們的問(wèn)卷調(diào)查:</h2>{% if all_questionslist %}<ul> {% for question in all_questionslist %} <li> <a href="{% url 'app01_myPolls:details' question.id %}">{{ question.question_text }}</a> </li> {% endfor %}</ul>{% else %}<p>還沒有調(diào)查問(wèn)卷!</p>{% endif %}</div></body></html>
這個(gè)H5文件在頭文件head位置引入了css文件,這個(gè)我們之后會(huì)介紹它具體是怎么實(shí)現(xiàn)的,暫時(shí)按下不表。我們按照自上而下的順序,介紹整個(gè)H5文件中代碼的含義:
A,我們?cè)谥黧wbody位置增加了一個(gè)id標(biāo)記為"container"的div盒子,目的是希望實(shí)現(xiàn)網(wǎng)頁(yè)內(nèi)容的居中樣式效果。
B,{% if all_questionslist %} + {% else %} + {% endif %} 就是典型的Django模版的格式,Django項(xiàng)目會(huì)自動(dòng)將代碼解讀為一個(gè) if + else + end的組合。這句話的意思是說(shuō),如果我們傳入的變量 all_questionslist 存在的話,就執(zhí)行 for循環(huán);如果不存在,就返回“還沒有調(diào)查問(wèn)卷!”
C,{% for question in all_questionslist %} + {% endfor %} 是for循環(huán),其中的代碼是羅列出所有的問(wèn)卷題目,并且設(shè)定它們點(diǎn)擊之后的網(wǎng)址url是指向特定問(wèn)卷的具體投票網(wǎng)頁(yè)的。
D,上面代碼中的雙大括號(hào)形式 {{ question.question_text }} 是Django模版語(yǔ)言中的屬性訪問(wèn)語(yǔ)法,采用英文句點(diǎn)的形式訪問(wèn)變量的屬性,其中question是視圖通過(guò)字典形式傳遞給模版的變量,通過(guò)句點(diǎn)“.”訪問(wèn)question的屬性question_text。
我們看下問(wèn)卷調(diào)查網(wǎng)站首頁(yè)的樣子:
因?yàn)閏ss文件還沒有設(shè)置,所以網(wǎng)頁(yè)展示的樣式還比較原始。我們后面會(huì)通過(guò)css文件的配置來(lái)優(yōu)化網(wǎng)頁(yè)的樣式,讓它顯得更美觀一些。
我們有3個(gè)視圖函數(shù),自然需要三個(gè).html網(wǎng)頁(yè)模板。index視圖對(duì)應(yīng)的index.html模板文件已經(jīng)分享過(guò)了,還需要分享details.html文件的代碼:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>投票頁(yè)面</title> {% load static %} <link rel="stylesheet" type='text/css' href="{% static 'style.css' %}" /></head><body><div id="container"> <br><h2>{{ question.question_text }}</h2><form action="{% url 'app01_myPolls:vote' question.id %}" method="post"> {% csrf_token %} {% for choice in question.choices_set.all %} <input type="radio" name="choice" id="choices{{ forloop.counter }}" value="{{ choice.id }}" > <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label> <br> {% endfor %}<br><br> <input type="submit" value="提交" /></form><br></div></body></html>
以及results.html文件中的代碼:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>投票結(jié)果頁(yè)面</title> {% load static %} <link rel="stylesheet" type='text/css' href="{% static 'style.css' %}" /></head><body><br><div id="container"><h1>{{ question.question_text }}</h1><ul> {% for choice in question.choices_set.all %} <li>{{ choice.choice_text }} -- 記票{{ choice.votes }}次</li> <br> {% endfor %}</ul><br><a href="{% url 'app01_myPolls:index' %}">返回首頁(yè)繼續(xù)投票</a></div></body></html>
所有三個(gè).html文件都內(nèi)置了.css,
{% load static %} <link rel="stylesheet" type='text/css' href="{% static 'style.css' %}" />
我們要在static文件夾中新建.css文件,輸入下面這段代碼:
li { color: green; text-decoration:none; list-style-type:decimal;}html { height:100%;}body { background-color: rgb(255,255,255); height:100%;}#container { background-color: rgb(245,245,250); width:600px; height:100%; margin: 0 auto;}h2 { text-align: center;}span { margin-left: 230px;}
我們一起感受一下增加了樣式的網(wǎng)站首頁(yè)的呈現(xiàn)效果:
投票網(wǎng)頁(yè)的效果如下:
結(jié)果網(wǎng)頁(yè)的效果:
經(jīng)過(guò)兩輪投票,針對(duì)第一個(gè)問(wèn)題“你喜歡心理學(xué)嗎?”我都點(diǎn)了喜歡。我們來(lái)看看數(shù)據(jù)庫(kù)里邊的記錄情況:
如果想把當(dāng)前Django項(xiàng)目部署到阿里云服務(wù)器,可以參考我之前寫的一個(gè)教程:
寫到這里,這個(gè)硬核教程就寫完了,希望這個(gè)教程能夠開啟您的互聯(lián)網(wǎng)夢(mèng)想??
有問(wèn)題可以加QQ或微信一起探討:10045198
關(guān)鍵詞:文章,系列,持續(xù),更新,創(chuàng)建