Django4.0 開始-編寫你的第一個Django應用,第2部分

2022-03-29 16:12 更新

數(shù)據(jù)庫配置

現(xiàn)在,打開 ?mysite/settings.py? 。這是個包含了 Django 項目設置的 Python 模塊。
通常,這個配置文件使用 SQLite 作為默認數(shù)據(jù)庫。如果你不熟悉數(shù)據(jù)庫,或者只是想嘗試下 Django,這是最簡單的選擇。Python 內(nèi)置 SQLite,所以你無需安裝額外東西來使用它。當你開始一個真正的項目時,你可能更傾向使用一個更具擴展性的數(shù)據(jù)庫,例如 PostgreSQL,避免中途切換數(shù)據(jù)庫這個令人頭疼的問題。
如果你想使用其他數(shù)據(jù)庫,你需要安裝合適的 ?database bindings? ,然后改變設置文件中 ?DATABASES 'default'? 項目中的一些鍵值:

  • ?ENGINE ?-- 可選值有 ?'django.db.backends.sqlite3'?,?'django.db.backends.postgresql'?,?'django.db.backends.mysql'?,或 ?'django.db.backends.oracle'?。
  • ?NAME ?-- 數(shù)據(jù)庫的名稱。如果你使用 SQLite,數(shù)據(jù)庫將是你電腦上的一個文件,在這種情況下,?NAME應該是此文件完整的絕對路徑,包括文件名。默認值 ?BASE_DIR / 'db.sqlite3'? 將把數(shù)據(jù)庫文件儲存在項目的根目錄。

如果你不使用 SQLite,則必須添加一些額外設置,比如 ?USER ?、 ?PASSWORD ?、 ?HOST ?等等。

編輯 ?mysite/settings.py? 文件前,先設置 ?TIME_ZONE為你自己時區(qū)。

此外,關(guān)注一下文件頭部的 ?INSTALLED_APPS設置項。這里包括了會在你項目中啟用的所有 Django 應用。應用能在多個項目中使用,你也可以打包并且發(fā)布應用,讓別人使用它們。
通常, ?INSTALLED_APPS默認包括了以下 Django 的自帶應用:

  • ?django.contrib.admin? -- 管理員站點, 你很快就會使用它。
  • ?django.contrib.auth? -- 認證授權(quán)系統(tǒng)。
  • ?django.contrib.contenttypes? -- 內(nèi)容類型框架。
  • ?django.contrib.sessions? -- 會話框架。
  • ?django.contrib.messages? -- 消息框架。
  • ?django.contrib.staticfiles? -- 管理靜態(tài)文件的框架。

這些應用被默認啟用是為了給常規(guī)項目提供方便。

默認開啟的某些應用需要至少一個數(shù)據(jù)表,所以,在使用他們之前需要在數(shù)據(jù)庫中創(chuàng)建一些表。請執(zhí)行以下命令:

...\> py manage.py migrate

這個 ?migrate命令查看 ?INSTALLED_APPS ?配置,并根據(jù) ?mysite/settings.py? 文件中的數(shù)據(jù)庫配置和隨應用提供的數(shù)據(jù)庫遷移文件(我們將在后面介紹這些),創(chuàng)建任何必要的數(shù)據(jù)庫表。你會看到它應用的每一個遷移都有一個消息。如果你有興趣,運行你的數(shù)據(jù)庫的命令行客戶端,輸入? \dt? (PostgreSQL), ?SHOW TABLES;? (MariaDB,MySQL),? .tables? (SQLite)或 ?SELECT TABLE_NAME FROM USER_TABLES;? (Oracle)來顯示 Django 創(chuàng)建的表。

創(chuàng)建模型

在 Django 里寫一個數(shù)據(jù)庫驅(qū)動的 Web 應用的第一步是定義模型 - 也就是數(shù)據(jù)庫結(jié)構(gòu)設計和附加的其它元數(shù)據(jù)。

在這個投票應用中,需要創(chuàng)建兩個模型:問題 ?Question ?和選項 ?Choice?。?Question ?模型包括問題描述和發(fā)布時間。?Choice ?模型有兩個字段,選項描述和當前得票數(shù)。每個選項屬于一個問題。
這些概念可以通過一個 Python 類來描述。按照下面的例子來編輯 ?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)

每個模型被表示為 ?django.db.models.Model? 類的子類。每個模型有許多類變量,它們都表示模型里的一個數(shù)據(jù)庫字段。
每個字段都是 ?Field類的實例 - 比如,字符字段被表示為 ?CharField,日期時間字段被表示為 ?DateTimeField。這將告訴 Django 每個字段要處理的數(shù)據(jù)類型。

每個 Field 類實例變量的名字(例如 ?question_text ?或 ?pub_date ?)也是字段名,所以最好使用對機器友好的格式。你將會在 Python 代碼里使用它們,而數(shù)據(jù)庫會將它們作為列名。
你可以使用可選的選項來為 Field 定義一個人類可讀的名字。這個功能在很多 Django 內(nèi)部組成部分中都被使用了,而且作為文檔的一部分。如果某個字段沒有提供此名稱,Django 將會使用對機器友好的名稱,也就是變量名。在上面的例子中,我們只為 ?Question.pub_date? 定義了對人類友好的名字。對于模型內(nèi)的其它字段,它們的機器友好名也會被作為人類友好名使用。

定義某些 ?Field ?類實例需要參數(shù)。例如 ?CharField ?需要一個 ?max_length ?參數(shù)。這個參數(shù)的用處不止于用來定義數(shù)據(jù)庫結(jié)構(gòu),也用于驗證數(shù)據(jù),我們稍后將會看到這方面的內(nèi)容。
?Field ?也能夠接收多個可選參數(shù);在上面的例子中:我們將 ?votes ?的 ?default ?也就是默認值,設為0。
注意在最后,我們使用 ?ForeignKey ?定義了一個關(guān)系。這將告訴 Django,每個 ?Choice ?對象都關(guān)聯(lián)到一個 ?Question ?對象。Django 支持所有常用的數(shù)據(jù)庫關(guān)系:多對一、多對多和一對一。

激活模型

上面的一小段用于創(chuàng)建模型的代碼給了 Django 很多信息,通過這些信息,Django 可以:

  • 為這個應用創(chuàng)建數(shù)據(jù)庫 ?schema?(生成 ?CREATE TABLE? 語句)。
  • 創(chuàng)建可以與 ?Question ?和 ?Choice ?對象進行交互的 Python 數(shù)據(jù)庫 API。

但是首先得把 ?polls ?應用安裝到我們的項目里。

為了在我們的工程中包含這個應用,我們需要在配置類 ?INSTALLED_APPS ?中添加設置。因為 ?PollsConfig ?類寫在文件 ?polls/apps.py? 中,所以它的點式路徑是 ?'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 應用。接著運行下面的命令:

...\> py manage.py makemigrations polls

你將會看到類似于下面這樣的輸出:

Migrations for 'polls':
  polls/migrations/0001_initial.py
    - Create model Question
    - Create model Choice

通過運行 ?makemigrations ?命令,Django 會檢測你對模型文件的修改(在這種情況下,你已經(jīng)取得了新的),并且把修改的部分儲存為一次 遷移。
遷移是 Django 對于模型定義(也就是你的數(shù)據(jù)庫結(jié)構(gòu))的變化的儲存形式 - 它們其實也只是一些你磁盤上的文件。如果你想的話,你可以閱讀一下你模型的遷移數(shù)據(jù),它被儲存在 ?polls/migrations/0001_initial.py? 里。別擔心,你不需要每次都閱讀遷移文件,但是它們被設計成人類可讀的形式,這是為了便于你手動調(diào)整 Django 的修改方式。
Django 有一個自動執(zhí)行數(shù)據(jù)庫遷移并同步管理你的數(shù)據(jù)庫結(jié)構(gòu)的命令 - 這個命令是 ?migrate?,我們馬上就會接觸它 - 但是首先,讓我們看看遷移命令會執(zhí)行哪些 SQL 語句。?sqlmigrate ?命令接收一個遷移的名稱,然后返回對應的 SQL:

...\> py manage.py sqlmigrate polls 0001

你將會看到類似下面這樣的輸出(我把輸出重組成了人類可讀的格式):

BEGIN;
--
-- 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
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL,
    "question_id" integer NOT NULL
);
ALTER TABLE "polls_choice"
  ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
    FOREIGN KEY ("question_id")
    REFERENCES "polls_question" ("id")
    DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");

COMMIT;

請注意以下幾點:

  • 輸出的內(nèi)容和你使用的數(shù)據(jù)庫有關(guān),上面的輸出示例使用的是 PostgreSQL。
  • 數(shù)據(jù)庫的表名是由應用名(?polls?)和模型名的小寫形式( ?question ?和 ?choice?)連接而來。(如果需要,你可以自定義此行為。)主鍵(IDs)會被自動創(chuàng)建。(當然,你也可以自定義。)
  • 默認的,Django 會在外鍵字段名后追加字符串 ?"_id"? 。(同樣,這也可以自定義。)
  • 外鍵關(guān)系由 ?FOREIGN KEY? 生成。你不用關(guān)心 ?DEFERRABLE部分,它只是告訴 PostgreSQL,請在事務全都執(zhí)行完之后再創(chuàng)建外鍵關(guān)系。
  • 生成的 SQL 語句是為你所用的數(shù)據(jù)庫定制的,所以那些和數(shù)據(jù)庫有關(guān)的字段類型,比如 ?auto_increment(MySQL)、 ?serial ?(PostgreSQL)和 ?integer primary key ?autoincrement? ?(SQLite),Django 會幫你自動處理。那些和引號相關(guān)的事情 - 例如,是使用單引號還是雙引號 - 也一樣會被自動處理。
  • 這個 ?sqlmigrate命令并沒有真正在你的數(shù)據(jù)庫中的執(zhí)行遷移 - 相反,它只是把命令輸出到屏幕上,讓你看看 Django 認為需要執(zhí)行哪些 SQL 語句。這在你想看看 Django 到底準備做什么,或者當你是數(shù)據(jù)庫管理員,需要寫腳本來批量處理數(shù)據(jù)庫時會很有用。

如果你感興趣,你也可以試試運行 ?python manage.py check ;?這個命令幫助你檢查項目中的問題,并且在檢查過程中不會對數(shù)據(jù)庫進行任何操作。
現(xiàn)在,再次運行 ?migrate命令,在數(shù)據(jù)庫里創(chuàng)建新定義的模型的數(shù)據(jù)表:

...\> py manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Rendering model states... DONE
  Applying polls.0001_initial... OK

這個 ?migrate命令選中所有還沒有執(zhí)行過的遷移(Django 通過在數(shù)據(jù)庫中創(chuàng)建一個特殊的表 ?django_migrations來跟蹤執(zhí)行過哪些遷移)并應用在數(shù)據(jù)庫上 - 也就是將你對模型的更改同步到數(shù)據(jù)庫結(jié)構(gòu)上。
遷移是非常強大的功能,它能讓你在開發(fā)過程中持續(xù)的改變數(shù)據(jù)庫結(jié)構(gòu)而不需要重新刪除和創(chuàng)建表 - 它專注于使數(shù)據(jù)庫平滑升級而不會丟失數(shù)據(jù)。我們會在后面的教程中更加深入的學習這部分內(nèi)容,現(xiàn)在,你只需要記住,改變模型需要這三步:

  • 編輯 ?models.py? 文件,改變模型。
  • 運行 ?python manage.py makemigrations? 為模型的改變生成遷移文件。
  • 運行 ?python manage.py migrate? 來應用數(shù)據(jù)庫遷移。

數(shù)據(jù)庫遷移被分解成生成和應用兩個命令是為了讓你能夠在代碼控制系統(tǒng)上提交遷移數(shù)據(jù)并使其能在多個應用里使用;這不僅僅會讓開發(fā)更加簡單,也給別的開發(fā)者和生產(chǎn)環(huán)境中的使用帶來方便。

初試API

現(xiàn)在讓我們進入交互式 Python 命令行,嘗試一下 Django 為你創(chuàng)建的各種 API。通過以下命令打開 Python 命令行:

...\> py manage.py shell

我們使用這個命令而不是簡單的使用“python”是因為 ?manage.py? 會設置 ?DJANGO_SETTINGS_MODULE環(huán)境變量,這個變量會讓 Django 根據(jù) ?mysite/settings.py? 文件來設置 Python 包的導入路徑。
當你成功進入命令行后,來試試 數(shù)據(jù)庫 API 吧:

>>> from polls.models import Choice, Question  # 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.
>>> 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 (1)>]>

?<Question: Question object (1)>? 對于我們了解這個對象的細節(jié)沒什么幫助。讓我們通過編輯 ?Question ?模型的代碼(位于 ?polls/models.py? 中)來修復這個問題。給 ?Question和 ?Choice增加 ?__str__()? 方法。

from django.db import models

class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text

class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text

給模型增加 ?__str__()? 方法是很重要的,這不僅僅能給你在命令行里使用帶來方便,Django 自動生成的 admin 里也使用這個方法來表示對象。
讓我們再為此模型添加一個自定義方法:

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)

新加入的 ?import datetime? 和 ?from django.utils import timezone? 分別導入了 Python 的標準 ?datetime模塊和 Django 中和時區(qū)相關(guān)的 ?django.utils.timezone? 工具模塊。
保存文件然后通過 ?python manage.py shell命令再次打開 Python 交互式命令行:

>>> from polls.models import Choice, Question

# 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()

介紹Django管理頁面

為你的員工或客戶生成一個用戶添加,修改和刪除內(nèi)容的后臺是一項缺乏創(chuàng)造性和乏味的工作。因此,Django 全自動地根據(jù)模型創(chuàng)建后臺界面。
Django 產(chǎn)生于一個公眾頁面和內(nèi)容發(fā)布者頁面完全分離的新聞類站點的開發(fā)過程中。站點管理人員使用管理系統(tǒng)來添加新聞、事件和體育時訊等,這些添加的內(nèi)容被顯示在公眾頁面上。Django 通過為站點管理人員創(chuàng)建統(tǒng)一的內(nèi)容編輯界面解決了這個問題。
管理界面不是為了網(wǎng)站的訪問者,而是為管理者準備的。

創(chuàng)建一個管理員賬號

首先,我們得創(chuàng)建一個能登錄管理頁面的用戶。請運行下面的命令:

...\> py manage.py createsuperuser

鍵入你想要使用的用戶名,然后按下回車鍵:

Username: admin

然后提示你輸入想要使用的郵件地址:

Email address: admin@example.com

最后一步是輸入密碼。你會被要求輸入兩次密碼,第二次的目的是為了確認第一次輸入的確實是你想要的密碼。

Password: **********
Password (again): *********
Superuser created successfully.

啟動開發(fā)服務器

Django 的管理界面默認就是啟用的。讓我們啟動開發(fā)服務器,看看它到底是什么樣的。

如果開發(fā)服務器未啟動,用以下命令啟動它:

...\> py manage.py runserver

現(xiàn)在,打開瀏覽器,轉(zhuǎn)到你本地域名的 “/admin/” 目錄, -- 比如 http://127.0.0.1:8000/admin/ 。你應該會看見管理員登錄界面:

admin01

因為翻譯功能默認是開啟的,如果你設置了 ?LANGUAGE_CODE?,登錄界面將顯示你設置的語言(如果 Django 有相應的翻譯)。

進入管理站點頁面

現(xiàn)在,試著使用你在上一步中創(chuàng)建的超級用戶來登錄。然后你將會看到 Django 管理頁面的索引頁:

admin02

你將會看到幾種可編輯的內(nèi)容:組和用戶。它們是由 ?django.contrib.auth? 提供的,這是 Django 開發(fā)的認證框架。

向管理頁面中加入投票應用

但是我們的投票應用在哪呢?它沒在索引頁面里顯示。
只需要再做一件事:我們得告訴管理,問題 ?Question ?對象需要一個后臺接口。打開 ?polls/admin.py? 文件,把它編輯成下面這樣:

from django.contrib import admin

from .models import Question

admin.site.register(Question)

體驗便捷的管理功能

現(xiàn)在我們向管理頁面注冊了問題 ?Question類。Django 知道它應該被顯示在索引頁里:

admin03t

點擊 "Questions" ?,F(xiàn)在看到是問題 "Questions" 對象的列表 "change list" 。這個界面會顯示所有數(shù)據(jù)庫里的問題 Question 對象,你可以選擇一個來修改。這里現(xiàn)在有我們在上一部分中創(chuàng)建的 “What's up?” 問題。

admin04t

點擊 “What's up?” 來編輯這個問題(Question)對象:

admin05t

注意事項:

  • 這個表單是從問題 ?Question ?模型中自動生成的
  • 不同的字段類型(日期時間字段 ?DateTimeField ?、字符字段 ?CharField?)會生成對應的 HTML 輸入控件。每個類型的字段都知道它們該如何在管理頁面里顯示自己。
  • 每個日期時間字段 ?DateTimeField ?都有 JavaScript 寫的快捷按鈕。日期有轉(zhuǎn)到今天(Today)的快捷按鈕和一個彈出式日歷界面。時間有設為現(xiàn)在(Now)的快捷按鈕和一個列出常用時間的方便的彈出式列表。

頁面的底部提供了幾個選項:

  • 保存(?Save?) - 保存改變,然后返回對象列表。
  • 保存并繼續(xù)編輯(?Save and continue editing?) - 保存改變,然后重新載入當前對象的修改界面。
  • 保存并新增(?Save and add another?) - 保存改變,然后添加一個新的空對象并載入修改界面。
  • 刪除(?Delete?) - 顯示一個確認刪除頁面。

如果顯示的 “發(fā)布日期(Date Published)” 和你在 教程 1 里創(chuàng)建它們的時間不一致,這意味著你可能沒有正確的設置 ?TIME_ZONE。改變設置,然后重新載入頁面看看是否顯示了正確的值。
通過點擊 “今天(Today)” 和 “現(xiàn)在(Now)” 按鈕改變 “發(fā)布日期(Date Published)”。然后點擊 “保存并繼續(xù)編輯(Save and add another)”按鈕。然后點擊右上角的 “歷史(History)”按鈕。你會看到一個列出了所有通過 Django 管理頁面對當前對象進行的改變的頁面,其中列出了時間戳和進行修改操作的用戶名:

admin06t


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號