本章我們將首次使用數(shù)據(jù)庫(kù)來(lái)構(gòu)建基本的留言板應(yīng)用,用戶(hù)可以在其中發(fā)布和閱讀短消息。我們將探索Django強(qiáng)大的內(nèi)置管理界面,它提供了可視化的方式來(lái)對(duì)我們的數(shù)據(jù)進(jìn)行修改。
強(qiáng)大的Django ORM(Object-Relational Mapper)內(nèi)置了對(duì)多個(gè)數(shù)據(jù)庫(kù)后端的支持。PostgreSQL、MySQL、MariaDB、Oracle或SQLite。這意味著可以在models.py文件中編寫(xiě)相同的Python代碼,它將自動(dòng)正確翻譯成每個(gè)數(shù)據(jù)庫(kù)。唯一需要的配置是更新我們config/settings.py文件中的DATABASES部分。
對(duì)于本地開(kāi)發(fā),Django默認(rèn)使用SQLite,因?yàn)樗腔谖募模虼耸褂闷饋?lái)比其他數(shù)據(jù)庫(kù)選項(xiàng)簡(jiǎn)單得多。
建立留言板應(yīng)用
$ mkdir mb && cd mb
$ django-admin startproject config .
$ python manage.py startapp posts
$ vi config/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'posts', # new
]
$ python manage.py migrate
為了確保數(shù)據(jù)庫(kù)反映項(xiàng)目的當(dāng)前狀態(tài),你需要在每次更新模型時(shí)運(yùn)行migrate(以及makemigrations)。
創(chuàng)建數(shù)據(jù)庫(kù)模型
創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)模型,在那里我們可以存儲(chǔ)和顯示來(lái)自用戶(hù)的帖子。Django的ORM會(huì)自動(dòng)為我們把這個(gè)模型變成數(shù)據(jù)庫(kù)表。
post/models.py文件
# posts/models.py
from django.db import models
class Post(models.Model):
text = models.TextField()
我們已經(jīng)創(chuàng)建了數(shù)據(jù)庫(kù)模型,叫做Post,它有一個(gè)數(shù)據(jù)庫(kù)字段text。我們還指定了它要容納的內(nèi)容類(lèi)型,即TextField()。Django提供了許多支持常見(jiàn)內(nèi)容類(lèi)型的模型字段,如字符、日期、整數(shù)、電子郵件等等。
我們用makemigrations命令創(chuàng)建一個(gè)遷移文件。遷移文件為數(shù)據(jù)庫(kù)模型的任何變化創(chuàng)建參考,這意味著我們可以跟蹤變化,并在必要時(shí)調(diào)試錯(cuò)誤。
$ python manage.py makemigrations posts
$ python manage.py migrate
如果運(yùn)行 python manage.py makemigrations,就會(huì)為整個(gè)Django項(xiàng)目創(chuàng)建遷移文件。

Django管理
Django的殺手锏之一是其強(qiáng)大的內(nèi)置管理界面,它提供了可視化的方式來(lái)與數(shù)據(jù)互動(dòng)。Django最初是作為報(bào)紙CMS(Content Management System內(nèi)容管理系統(tǒng))而建立的。當(dāng)時(shí)的想法是,記者們可以在管理編寫(xiě)和編輯他們的故事,而不需要接觸 "代碼"。隨著時(shí)間的推移,內(nèi)置的管理應(yīng)用程序已經(jīng)發(fā)展成為神奇的、開(kāi)箱即用的工具,用于管理Django項(xiàng)目的各個(gè)方面。
要使用Django管員,我們首先需要?jiǎng)?chuàng)建超級(jí)用戶(hù)。在你的命令行控制臺(tái)中,輸入python manage.py createsuperuser,并對(duì)提示的用戶(hù)名、電子郵件和密碼做出回應(yīng)。
$ python manage.py createsuperuser
用python manage.py runserver重啟Django服務(wù)器,在你的網(wǎng)頁(yè)瀏覽器中進(jìn)入http://127.0.0.1:8000/admin/。你應(yīng)該看到管理員的登錄界面。

但是我們的帖子沒(méi)有顯示在管理主頁(yè)面上!必須更新一個(gè)應(yīng)用程序的admin.py文件,使其出現(xiàn)在管理員中。
在你的文本編輯器中打開(kāi)post/admin.py,添加以下代碼,以便顯示Post模型。
# posts/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
Django現(xiàn)在知道它應(yīng)該在管理頁(yè)面上顯示我們的post應(yīng)用程序和它的數(shù)據(jù)庫(kù)模型Post。如果你刷新你的瀏覽器,你會(huì)看到它出現(xiàn)了。

點(diǎn)擊帖子對(duì)面的+添加按鈕,在文本表格字段中輸入你自己的內(nèi)容。

然后點(diǎn)擊 "保存 "按鈕,這將重定向到主帖子頁(yè)面。然而,如果你仔細(xì)觀察,就會(huì)發(fā)現(xiàn)問(wèn)題:新條目被稱(chēng)為 "Post object",這不是很好的描述!

讓我們來(lái)改變它。在 posts/models.py 文件中,添加一個(gè)新的函數(shù) str,如下所示。
# posts/models.py
from django.db import models
class Post(models.Model):
text = models.TextField()
def __str__(self):
return self.text[:50]
這將顯示文本字段的前50個(gè)字符。

視圖/模板/URLs
現(xiàn)在我們想列出我們數(shù)據(jù)庫(kù)模型的內(nèi)容,Django配備了基于通用類(lèi)的ListView。
在post/views.py文件中輸入下面的Python代碼。
# posts/views.py
from django.views.generic import ListView
from .models import Post
class HomePageView(ListView):
model = Post
template_name = 'home.html'
增加模板
$ mkdir templates
$ vi templates/home.html
<!-- templates/home.html -->
<h1>Message board homepage</h1>
<ul>
{% for post in object_list %}
<li>{{ post.text }}</li>
{% endfor %}
</ul>
最后一步是設(shè)置我們的URLConfs。
config/urls.py文件
# config/urls.py
from django.contrib import admin
from django.urls import path, include # new
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('posts.urls')), # new
]
然后在post應(yīng)用程序中創(chuàng)建urls.py文件。
# posts/urls.py
from django.urls import path
from .views import HomePageView
urlpatterns = [
path('', HomePageView.as_view(), name='home'),
]
用python manage.py runserver重新啟動(dòng)服務(wù)器,并導(dǎo)航到我們的主頁(yè),現(xiàn)在它列出了我們的留言板帖子。

測(cè)試
以前,我們只測(cè)試靜態(tài)頁(yè)面,所以我們使用SimpleTestCase。但現(xiàn)在有數(shù)據(jù)庫(kù),需要使用TestCase。我們不需要在我們的實(shí)際數(shù)據(jù)庫(kù)上運(yùn)行測(cè)試,而是可以建立一個(gè)單獨(dú)的測(cè)試數(shù)據(jù)庫(kù),用樣本數(shù)據(jù)填充,然后針對(duì)它進(jìn)行測(cè)試,這肯定是更安全、更快的方法。
# posts/tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Post
class PostModelTest(TestCase):
def setUp(self):
Post.objects.create(text='just a test')
def test_text_content(self):
post=Post.objects.get(id=1)
expected_object_name = f'{post.text}'
self.assertEqual(expected_object_name, 'just a test')
class HomePageViewTest(TestCase): # new
def setUp(self):
Post.objects.create(text='this is another test')
def test_view_url_exists_at_proper_location(self):
resp = self.client.get('/')
self.assertEqual(resp.status_code, 200)
def test_view_url_by_name(self):
resp = self.client.get(reverse('home'))
self.assertEqual(resp.status_code, 200)
def test_view_uses_correct_template(self):
resp = self.client.get(reverse('home'))
self.assertEqual(resp.status_code, 200)
self.assertTemplateUsed(resp, 'home.html')