接著我們的上一篇文章繼續(xù)往下進(jìn)行.在這一篇文章當(dāng)中我們會(huì)設(shè)置一個(gè)數(shù)據(jù)庫(kù),創(chuàng)建我們的第一個(gè)model.并且快速的介紹一下Django自動(dòng)生成admin站點(diǎn).
數(shù)據(jù)庫(kù)設(shè)置
首先打開(kāi)mysite/settings.py
文件, setting.py
是存儲(chǔ)Django設(shè)置信息的地方.
簡(jiǎn)單介紹一下settings.py
文件里INSTALLED_APPS
的含義:
django.contrib.admin : #admin站點(diǎn), 稍后我們會(huì)用到
django.contrib.auth : #authentication系統(tǒng)
django.contrib.contenttypes : #content types的框架
django.contrib.sessions : #session框架
django.contrib.messages :#messaging框架
django.contrib.staticfiles : #管理靜態(tài)文件的框架
在這些應(yīng)用程序中至少需要使用一個(gè)數(shù)據(jù)庫(kù), 所以在使用之前我們需要先創(chuàng)建數(shù)據(jù)庫(kù).只要運(yùn)行下面的代碼就可以了:
python manage.py migrate
migrate
指令會(huì)查看INSTALLED_APPS
中的設(shè)置,并根據(jù)mysite/settings.py
文件中的設(shè)置創(chuàng)建數(shù)據(jù)庫(kù)并將數(shù)據(jù)庫(kù)關(guān)聯(lián)到APP.
創(chuàng)建models
接下來(lái)會(huì)定義我們的models.
在我們的poll APP 中會(huì)創(chuàng)建兩個(gè)模型Question
和Choice
.
Question
有一個(gè)問(wèn)題的描述字段和發(fā)布日期字段.Choice
有一個(gè)文本描述字段和和統(tǒng)計(jì)字段.每一個(gè)Choice
都關(guān)聯(lián)著一個(gè)'Question'.
這是最簡(jiǎn)單的Pyhton類.按照下面的樣子編輯polls/models.py
文件.
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
代碼非常的簡(jiǎn)單明了,每一個(gè)model都是的django.db.models.Model
的子類.每一個(gè)model都有幾個(gè)變量, 每一個(gè)變量都對(duì)應(yīng)著model在數(shù)據(jù)庫(kù)中的一個(gè)字段.
每一個(gè)field都是 Field
類的一個(gè)實(shí)例.CharField
是用來(lái)存儲(chǔ)字符的,DateTimeField
是用來(lái)存儲(chǔ)時(shí)間的.這些告訴了Django每一個(gè)字段存儲(chǔ)的數(shù)據(jù)類型.
每一個(gè)Field
實(shí)例的名字, 就是字段的名字(例如上面的question_text
和pub_date
).你會(huì)在Python的代碼中使用這些值, 數(shù)據(jù)庫(kù)則會(huì)使用這些字段名作為每一列的名字.
某些Field
需要洗一些必須的參數(shù), 比如 CharField
需要你指定max_length
.這些不僅是數(shù)據(jù)庫(kù)需要的信息,而且會(huì)用來(lái)做驗(yàn)證, 待會(huì)就知道了.
一個(gè)Field
也可以有幾個(gè)可選參數(shù).在這個(gè)例子中我們會(huì)將votes
的默認(rèn)值設(shè)置為0.
最后需要注意的是我們用ForeignKey
來(lái)定義兩個(gè)模型之間的關(guān)聯(lián)關(guān)系.這讓Django
知道每一個(gè)Choice
都關(guān)聯(lián)這一個(gè)單獨(dú)的Question
.
Django
支持所有的關(guān)系類型: 一對(duì)多, 多對(duì)都, 一對(duì)一.
激活models
上面那一點(diǎn)點(diǎn)代碼給了Django很多信息.根據(jù)上面的信息, Django能夠:
- 為我們的APP創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)的表
- 創(chuàng)建可以用Python訪問(wèn)數(shù)據(jù)庫(kù)的API來(lái)訪問(wèn)Question 和 Choice對(duì)象.
但是首先我們應(yīng)該讓我們的項(xiàng)目知道polls app已經(jīng)被安裝了.
為了將app包含進(jìn)我們項(xiàng)目里, 我們需要在
INSTALLED_APPS
中配置一個(gè)引用,PollsConfig
類在'polls/apps.py'文件中,所以用點(diǎn)語(yǔ)法的話, 他的路徑應(yīng)該是polls.apps.PollsConfig
, 編輯mysite/settings.py
文件, 并且將配置文件的路徑添加在INSTALLED_APPS
中:
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
現(xiàn)在Django知道他包含了polls APP,現(xiàn)在我們運(yùn)行另外一個(gè)命令:
ython manage.py makemigrations polls
你在終端中應(yīng)該看到下面這些輸出:
Migrations for 'polls':
polls/migrations/0001_initial.py:
- Create model Choice
- Create model Question
- Add field question to choice
運(yùn)行makemigrations
指令相當(dāng)于你告訴Django你的models做了一些改變并且你想保存這些改變.
Django會(huì)將你對(duì)Models的改變存儲(chǔ)為一個(gè)文件, 文件的名字和路徑polls/migrations/0001_initial.py
,你可以通過(guò)查看這個(gè)文件來(lái)查看你所做的改變.
migrate
命令可以讓數(shù)據(jù)庫(kù)自動(dòng)應(yīng)用這些改變,我們稍后就會(huì)用到.但是首先我們來(lái)看一下這些改變會(huì)用到哪些SQL語(yǔ)句.sqlmigrate
可以輸出這些改變名字, 并且輸出這些SQL語(yǔ)句.
python manage.py sqlmigrate polls 0001
你應(yīng)該會(huì)看到類似下面的輸出:
<pre>
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
"id" serial NOT NULL PRIMARY KEY,
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL
);
--
-- Create model Question
--
CREATE TABLE "polls_question" (
"id" serial NOT NULL PRIMARY KEY,
"question_text" varchar(200) NOT NULL,
"pub_date" timestamp with time zone NOT NULL
);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL;
ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT;
CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");
ALTER TABLE "polls_choice"
ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id"
FOREIGN KEY ("question_id")
REFERENCES "polls_question" ("id")
DEFERRABLE INITIALLY DEFERRED;
COMMIT;
</pre>
如果你有興趣的話你也可以運(yùn)行python manage.py check
,這個(gè)指令會(huì)檢查你項(xiàng)目中遇到的問(wèn)題,并且不會(huì)將這些改變應(yīng)用到數(shù)據(jù)庫(kù)上, 不會(huì)對(duì)數(shù)據(jù)庫(kù)造成任何影響.
現(xiàn)在, 再次運(yùn)行 migrate
指令, 來(lái)創(chuàng)建那些模型在數(shù)據(jù)庫(kù)中所對(duì)應(yīng)的table.
python manage.py migrate
migrate
指令會(huì)應(yīng)用之前沒(méi)有應(yīng)用的改變.
Migrations
指令非常有用,可以讓你在開(kāi)發(fā)項(xiàng)目的過(guò)程中隨時(shí)改變models,而不用刪除數(shù)據(jù)庫(kù)也造成任何數(shù)據(jù)的丟失.后面我們會(huì)深入討論這些內(nèi)容,但是現(xiàn)在請(qǐng)記住下面這三步對(duì)models做出的改變:
修改
models
(在models.py
文件中)運(yùn)行python manage.py makemigrations
來(lái)存儲(chǔ)這些改變運(yùn)行python manage.py migrate
將這些改變應(yīng)用到數(shù)據(jù)庫(kù)
之所以將將這些改變的存儲(chǔ)和應(yīng)用分成幾個(gè)指令是為了在你項(xiàng)目的版本控制系統(tǒng)中記錄這些操作.它不僅會(huì)讓你的開(kāi)發(fā)工作變得非常簡(jiǎn)單, 而且這對(duì)其他開(kāi)發(fā)者非常有用.
使用這些API
接下來(lái)我們進(jìn)入, Python與shell的交互環(huán)節(jié),并練習(xí)使用Django 給我們提供的API.使用下面的命令調(diào)用Python shell:
python manage.py shell
我們可以通過(guò)輸入簡(jiǎn)單的 python
指令代替上面的指令,因?yàn)? manage.py
文件里設(shè)置了DJANGO_SETTINGS_MODULE
這個(gè)環(huán)境變量.它給Django的mysite/settings.py
文件提供了Python的導(dǎo)入路徑.
注意:接下來(lái)的操作是在虛擬環(huán)境下進(jìn)行的, 因?yàn)闀r(shí)間的關(guān)系, 在完成上面的內(nèi)容后, 我退出了服務(wù)器的操作.后來(lái)在家繼續(xù)進(jìn)行接下來(lái)的操作的時(shí)候沒(méi)辦法繼續(xù)往下盡心.然后就想是不是沒(méi)有進(jìn)入虛擬環(huán)境的問(wèn)題,然后就進(jìn)到虛擬環(huán)境下在自己建的項(xiàng)目mystie目錄下繼續(xù)往下操作. 果然OK!
當(dāng)我們進(jìn)入了shell以后, 我們就可以瀏覽 database API:
>>> from polls.models import Question, Choice # Import the model classes we just wrote.
# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>
# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
# Save the object into the database. You have to call save() explicitly.
>>> q.save()
# Now it has an ID. Note that this might say "1L" instead of "1", depending
# on which database you're using. That's no biggie; it just means your
# database backend prefers to return integers as Python long integer
# objects.
>>> q.id
1
# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)
# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()
# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object>]>
一定要注意每一行語(yǔ)句前面的空格數(shù)量, 因?yàn)镻ython對(duì)代碼樣式的要求非常嚴(yán)格, 如果空格數(shù)量過(guò)多會(huì)包語(yǔ)法錯(cuò)誤.一定要注意.
<Question: Question object>
完全無(wú)法幫助我們了解Question
對(duì)象的詳細(xì)信息.所以接下來(lái), 我們來(lái)修復(fù)這個(gè)問(wèn)題, 編輯Question model
(在 polls/models.py 文件里)然后在Question
和Choice
添加一個(gè) str()
方法.當(dāng)然有些對(duì)Python語(yǔ)法不是很熟悉的同學(xué)可能不知道如何退出shell環(huán)境, 只需要在終端里輸入:
>>> exit()
就可以了.
編輯polls/models.py
文件:
vim polls/models.py
為我們的models添加str()
方法是非常重要的,它不僅在我們開(kāi)發(fā)的時(shí)候提供參考, 而且在Django自動(dòng)生成的admin在使用這些對(duì)象的時(shí)候也會(huì)用到.
這些只是普通的python方法, 讓我們添加一個(gè)自定義的方法, 哪怕僅僅只是做一個(gè)示范.
在polls/models.py
文件中, 加入下面的代碼:
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
注意開(kāi)頭添加的import datetime
和from django.utils import timezone
兩行代碼分別引用了Python標(biāo)準(zhǔn)的 datetime
模塊和Django中django.utils.timezone
時(shí)間相關(guān)的工具類.
保存這些改動(dòng).
這里給對(duì)Vim編輯器不熟悉的同學(xué)一些提示, 通過(guò)vim filename打開(kāi)文件后點(diǎn)擊i鍵可以進(jìn)入編輯模式, 如果要退出vim的編輯模式, 先點(diǎn)擊`esc`鍵, 然后點(diǎn)擊`:wq`可以保存并退出編輯模式.
保存上面的更改并通過(guò)鍵入python manage.py shell
重新進(jìn)入python的交互模式.
>>> from polls.models import Question, Choice
# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>
# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>
# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Question matching query does not exist.
# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>
# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)
# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>
# Create three choices.
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
# Choice objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>
# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
想要了解更多關(guān)于模型的信息可以參考 Accessing related objects.想要了解更多或者如何使用雙下劃線通過(guò)API查看字段信息,可以參考 Field lookups.想要了解更多的關(guān)于數(shù)據(jù)API的詳細(xì)信息可以參考 Database API reference.
Django Admin的介紹
創(chuàng)建一個(gè)admin用戶
首先我們需要?jiǎng)?chuàng)建一個(gè)可以登錄admin站點(diǎn)的用戶.運(yùn)行下面的這個(gè)命令:
python manage.py createsuperuser
輸入你想要的用戶名并且按enter鍵
Username: admin
然后會(huì)提示你輸入郵件地址
Email address: admin@example.com
最后一步是輸入你的密碼,會(huì)提示你輸入兩次, 第二次是確認(rèn)第一次輸入的密碼.
Password: **********
Password (again): *********
Superuser created successfully.
開(kāi)始開(kāi)發(fā)服務(wù)器
Django的admin站點(diǎn), 默認(rèn)是激活的.接下來(lái)我們開(kāi)始開(kāi)發(fā)服務(wù)器并且訪問(wèn)它.
如果服務(wù)器并沒(méi)有運(yùn)行起來(lái), 用下面的指令啟動(dòng)它:
python manage.py runserver
(如果按照之前我寫的從0開(kāi)始搭建nginx-uWSGI-Django-python服務(wù)器配置好了uWSGI的自啟動(dòng)服務(wù), 那么是不需要執(zhí)行上面的指令的站點(diǎn)默認(rèn)試運(yùn)行的')
在瀏覽器中輸入http://139.xxx.xxx.129:8000/admin/ 訪問(wèn)admin站點(diǎn).
你應(yīng)該會(huì)看到admin的登錄界面
進(jìn)入admin站點(diǎn)
現(xiàn)在用你上一步創(chuàng)建的超級(jí)用戶登錄admin站點(diǎn), 你應(yīng)該會(huì)看到Django admin站點(diǎn)的首頁(yè).
你應(yīng)該看到了可以編輯groups和users, 這些是有Django的與認(rèn)證相關(guān)的django.contrib.auth
框架提供的.
讓poll app在admin中可以編輯
但是我們Polls APP去哪里了呢?它并沒(méi)有顯示在admin站點(diǎn)的首頁(yè).
只要做一件事就可以了: 我們只用告訴 adminQuestion
對(duì)象是admin的一個(gè)接口就可以了.所以我們打開(kāi)polls/admin.py
文件, 并按照下面編輯:
from django.contrib import admin
from .models import Question
admin.site.register(Question)
探索自由的管理功能
現(xiàn)在我們注冊(cè)了Question
,現(xiàn)在Django知道了他應(yīng)該在首頁(yè)顯示Question
.
注意: 這里我遇到了一個(gè)坑, 就是我按照上面的步驟在admin.py中導(dǎo)入了Question 以后, 刷新admin首頁(yè)并沒(méi)有出現(xiàn)Question 列表, 而是再我重新啟動(dòng)服務(wù)器以后再訪問(wèn)的時(shí)候才出現(xiàn)了Question 列表
點(diǎn)擊Questions
, 現(xiàn)在你應(yīng)該位于questions
的修改修改列表頁(yè),這里顯示了數(shù)據(jù)庫(kù)中所有的questions你可以選擇并修改他們.這里有一個(gè)我們之前創(chuàng)建的“What’s up?”的question.
點(diǎn)擊“What’s up?” question并且編輯它.
這里有幾點(diǎn)需要注意:
- 這個(gè)表單是從Question model中自動(dòng)生成的
- model中不同類型的屬性字段((DateTimeField
, CharField
))有相應(yīng)類型的輸入框.Django的adimn站點(diǎn)知道如何正確的顯示不同類型的輸入框 - 每一個(gè) DateTimeField
都會(huì)有相應(yīng)的JavaScript的快捷按鈕, 日期有一個(gè)Today
的快捷按鈕并且有一個(gè)日歷的彈框,時(shí)間有一個(gè)Now
的快捷按鈕, 并且有一個(gè)快捷按鈕讓你可以選擇時(shí)間.
在這個(gè)頁(yè)面的底部有幾個(gè)選項(xiàng): - Save - 爆粗努修改并返回上一頁(yè)
- Save and continue editing - 保存修改并且重新加載這一頁(yè)
- Save and add another - 保存修改, 并且加載一個(gè)這種類型的對(duì)象的空表單.
- Delete - 顯示一個(gè)確認(rèn)刪除的頁(yè)面.
如果“Date published”的值與你在上一篇文章中創(chuàng)建的時(shí)候的時(shí)間不一致.
通過(guò)點(diǎn)擊 “Today” 和 “Now”按鈕改變“Date published”的值.然后點(diǎn)擊頁(yè)面下面的“Save and continue editing.”.然后點(diǎn)擊右上角“History” .你就會(huì)看到通過(guò)Django的admin站點(diǎn)對(duì)這個(gè)對(duì)象所有的修改歷史, 包括修改的時(shí)間和修改的人.
.
現(xiàn)在了你已經(jīng)熟悉了models 的API和admin站點(diǎn).在下移偏重我們會(huì)學(xué)習(xí)如何為我們的 polls app添加更多的views.