1. 程式人生 > >一個完整的Django入門指南

一個完整的Django入門指南

這一部分的教程介紹的是模型、檢視、模板、測試和管理

介紹

歡迎來到我們的Django教程的第二部分!在上一部分中,我們安裝了所有我們需要的東西。希望您已經安裝了Python 3.6,並在虛擬環境中執行Django 1.11。我們已經建立了一個我們要玩的專案。在這部分中,我們將繼續在同一個專案中編寫程式碼。

網頁版塊

我不知道你的情況,但我個人認為,通過看到實際的例子和程式碼片段,我能學到更多東西。對於我來說,在例子中你讀類A和類B,或者當我看到經典類foo(bar)例子時來處理一個概念是很難的。我不想和你一起這樣做。
所以, 在我們進入有趣的部分,玩模型,檢視和一切之前。讓我們花店時間,簡要的討論一下我們將要開發的這個專案。
如果您已經有了Web開發的經驗,並且覺得它太過詳細,您可以瀏覽一下圖片,瞭解我們將要構建的內容,然後跳到本教程的模型部分。
但是如果你是網路開發的新手,我強烈建議你繼續閱讀。它將使您對Web應用程式的建模和設計有一些很好的瞭解。總的來說,Web開發和軟體開發不僅僅是關於編碼的。

這裡寫圖片描述

  • 我知道我們要建造的不是火箭科學。
  • 但是即使是簡單的過程也需要一個適當的計劃。
  • 事實上,缺乏規劃是許多軟體專案失敗的主要原因之一。
用例圖

我們的專案是一個討論版塊(一個論壇)。整個想法要保持幾個版塊,就像類別一樣。然後,在一個特定的版塊中,使用者可以通過建立一個新的主題來開始新的討論。在這個主題中, 其他使用者可以參與討論釋出回覆。

我們需要找到一種方法來區分普通使用者和管理使用者,因為只有管理員才能建立新的版塊。下面,概述我們的主要用例和每種型別的角色使用者。

圖1:Web板提供的核心功能的用例圖

類圖

從用例圖中,我們可以開始考慮專案的實體。實體是我們要建立的模型,它與Django應用程式處理的資料密切相關。

為了使我們能夠實現上一節中描述的用例,我們需要至少實現以下模型:版塊(Board)、主題(Topic)、帖子(Post)、使用者(User

圖2:Web板的類圖草案

同樣重要的是,花點時間去思考模型是如何相互關聯的。實踐告訴我們的是,在一個主題(Topic)中,我們需要有一個欄位來確定它是屬於哪個版塊(Board)的。類似的,帖子(Post)需要一個欄位來表示它所屬的主題(Topic),這樣我們就可以在討論中只列出特定主題(Topic)中建立的帖子(Post)。最後,我們在主題(Topic)和帖子(Post)中都需要欄位來了解誰開始了討論,這樣我們就可以確定誰在釋出回覆。

我們還可以讓版塊(Board)和使用者(User

)模型建立聯絡,這樣我們就可以確定誰建立了一個版塊(Board)。但是這個資訊與應用程式無關。還有其他方法可以追蹤這些資訊,稍後你將看到。

既然我們已經有了基本的類表示,我們就必須考慮這些模型中的每一種資訊。這種事情很容易變的複雜。所以,試著把重點放在重要的部分上。你需要啟動開發資訊。稍後,我們可以使用遷移來改進模型,你將在下一篇教程中詳細的看到這一點。

但就目前而言,這將是我們模型欄位的基本表現:

圖3:類圖強調類(模型)之間的關係

這個類圖強調了模型之間的關係。這些行和箭頭最終將被轉換為以後的欄位。

對於版塊(Board)模型,我們將從兩個欄位開始:namedescriptionname 欄位必須是唯一的,這樣才能避免重複的名稱。description 只是為了給大家一個提示,說明版塊(Board)的一切。

主題(Topic)模型將由四個欄位組成:subjectlast update用於定義主題排序,topic starter用於定義啟動主題(Topic)的使用者(User),以及一個名為board的欄位,用來定義一個特定的主題屬於哪個版塊(Board)。

帖子(Post)模型有一個message欄位,用於儲存回覆的文字;一個created at日期時間欄位,用於主題(Topic)中帖子(Post)的排序;一個updated at日期時間欄位,來通知使用者何時以及是否編輯了新的帖子。我們還必須用同樣的日期時間欄位關聯使用者(User)模型:created byupdated by

最後,使用者(User)模型。在類圖中,我只提到了usernamepasswordemailis superuser標誌欄位,因為這是我們現在要使用的全部內容。需要注意的是,我們不需要建立一個使用者(User)模型,因為Django已經在設計包中內建了一個內建的使用者模型。我們要使用它。

關於類圖中的多樣性(數字1,0 .. *。等等),以下是說明:

這裡寫圖片描述
一個主題(Topic)必須與一個(1)版塊(Board)相關聯(這意味著它不能為空),並且一個版塊(Board)可能與許多主題(Topic)相關聯(0 .. *。)。這意味著一個版塊(Board)可能沒有任何一個主題(Topic)。

這裡寫圖片描述
一個主題(Topic)需要至少一個帖子(Post)(發起帖子),並且可能有許多帖子(Post)(1 .. *)。一個帖子(Post)必須有且只有一個主題(Topic)關聯。

這裡寫圖片描述
一個主題(Topic)必須有且只有一個使用者(User)關聯:主題發起者(1)。一個使用者(User)可能有許多或者沒有主題(Topic)(0 .. *)。

這裡寫圖片描述
一個帖子(Post)有且只有一個使用者(User)關聯:create by(1)。一個使用者(User)可能有許多或者沒有帖子(Post)(0 .. *)。帖子(Post)和使用者(User)之間的第二個關聯是一個直接關聯(參見行末尾的箭頭),這意味著我們只對關係的一端感興趣,這是使用者編輯帖子的內容。它將被翻譯成updated by欄位。多重數表示0..1,意思是更新的欄位可能是空的(這個帖子(Post)沒有編輯),最多隻能與一個使用者(User)關聯。

繪製這個類圖的另一種方法是強調欄位而不是模型之間的關係:
圖4:類圖強調類(模型)的屬性(欄位)

上面的表示與前面的相同,而且它也更接近我們將要使用Django模型API設計的內容。在這個表示中,我們可以清楚的看到,在帖子(Post)模型中,關聯欄位topiccreated byupdated by變成了模型欄位。另一件值得關注的事情是,在主題(Topic)模型中,我們有一個名為posts()的操作方法(類方法)。我將通過實現反向關係來實現這一點,Django將在資料庫中自動執行查詢,返回屬於某個特定主題的所有帖子的列表。

線框圖

在花了一些時間來設計應用程式模型之後,我喜歡建立一些線框圖來定義需要完成的工作,以及對我們將要進行的工作有一個清晰的瞭解。

這裡寫圖片描述

  • 有時,如果需求不清楚…
  • …或者如果實體之間的關係不明顯
  • 我們可以先畫線框

然後基於線框圖,我們可以對應用程式中設計的實體有更深的理解。

首先,我們需要在主頁上顯示所有的版塊:
這裡寫圖片描述

如果使用者點選一個連結,比如Django版,它應該列出所有的主題:
這裡寫圖片描述

這裡我們有兩條主要路徑:使用者點選“New topic”按鈕來建立一個新主題,或者使用者點選某個主題來檢視或參與討論。

“New topic”介面:
這裡寫圖片描述

主題介面,顯示文章和討論:
這裡寫圖片描述

如果使用者點選了Reply按鈕,他們將看到以下介面,帖子摘要的反向順序陳列(最新的第一個):
這裡寫圖片描述

模型

這些模型基本上是應用程式的資料庫佈局的表示。在這一節中,我們要做的是建立我們在上一節中建模的類的Django表示:Board、Topic和Post。User模型已經在一個名為auth的內建應用程式中定義,該應用程式在名為django.contrib.auth的名稱空間中列出。

我們將在 boards/models.py 檔案中完成所有的工作。下面是我們如何在Django應用程式中表示類圖:

from django.db import models
from django.contrib.auth.models import User

# 在翻譯這篇文章的時候,使用的是python 3.6.4, Django 2.0.2
# ForeignKey中有一個on_delete引數

class Board(models.Model):
    name = models.CharField(max_length=30, unique=True)
    description = models.CharField(max_length=100)

class Topic(models.Model):
    subject = models.CharField(max_length=255)
    last_updated = models.DateTimeField(auto_now_add=True)
    board = models.ForeignKey(Board, models.CASCADE, related_name='topics')
    starter = models.ForeignKey(User, models.CASCADE, related_name='topics')

class Post(models.Model):
    message = models.TextField(max_length=4000)
    topic = models.ForeignKey(Topic, models.CASCADE, related_name='posts')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(null=True)
    created_by = models.ForeignKey(User, models.CASCADE, related_name='posts')
    updated_by = models.ForeignKey(User, models.CASCADE, null=True, related_name='+')

所有的模型都是django.db.models.Model的子類。每個類將被轉換為資料庫表。每格欄位都是由django.db.models.Field(內建的Django核心)例項表示,並將被轉換為資料庫列。

CharFieldDateTimeField等欄位都是django.db.models.Field的子類,他們包含在Django核心中準備使用。

我們只使用了CharFieldTextFieldDateTimeFieldForeignKey欄位來定義我們的模型。但是Django還提供了各種各樣的選項來表示不同型別的資料,比如IntegerFieldBooleanFieldDecimalField。我們將根據需要使用他們。

有些欄位需要引數,比如CharField。我們應該始終設定一個max_length。此資訊將用於建立資料庫列。Django需要知道資料庫列需要有多大。max_length引數也將被Django Forms API用於驗證使用者輸入。稍後將進行更詳細的討論。

Board模型定義中,具體來說是在name欄位中,我們還設定了引數unique=True,顧名思義,它將強制在資料庫級執行欄位的唯一性。

Post模型中,created_at欄位有一個可選引數,auto_now_add設定為True。這將指示Django在建立Post物件時設定當前日期和時間。

建立模型之間的關係的一種方法是使用ForeignKey欄位。它將在模型之間建立一個連結,並在資料庫級別建立適當的關係。ForeignKey欄位的位置引數需要一個它將關聯的模型。

例如,在Topic模型中,board欄位對Board模型來說是一個外來鍵。它將告訴Django一個Topic例項只涉及一個Board例項。related_name引數將用於建立反向關係,在該關係中,Board例項將訪問屬於它的Topic例項的列表。

Django自動建立了這種反向關係——相關的名稱是可選的。但是,如果我們不為它設定一個名稱,Django將使用:(class_name)_set。例如,在Borad模型中,Topic例項將使用topic_set。相反,我們只是將其重新命名為topics,使其更自然。

Post模型中,updated_by欄位設定了related_name='+'。這告訴Django我們不需要這種反向關係,所以它會忽略它。

下面你可以看到類圖和原始碼之間的比較,用Django生成模型。綠線代表了我們處理逆向關係的方式。
這裡寫圖片描述

此時,你可能會想:“主鍵/IDs怎麼樣?”如果我們不指定一個模型的主鍵,Django會自動為我們生成它。所以現在我們很好。在下一節中,你將看到它是如何工作的。

遷移模型

下一步是告訴Django建立資料庫,這樣我們就可以開始使用它了。

python manage.py makemigrations

你肯能會得到這樣的輸出結果:

Migrations for 'boards':
  boards/migrations/0001_initial.py
    - Create model Board
    - Create model Post
    - Create model Topic
    - Add field topic to post
    - Add field upodated_by to post

至此,Django在boards/migrations中建立了一個名為0001_initial.py的檔案。它代表了我們應用程式的當前狀態。在接下來的步驟中,Django將使用這個檔案建立表和列。

遷移檔案被翻譯成SQL語句。如果你熟悉SQL,你可以執行以下命令來檢查資料庫中執行的SQL指令:

python manage.py sqlmigrate boards 0001

如果你對SQL不熟悉,請不要擔心。在本系列教程中,我們不會直接使用SQL。所有的工作都將使用Django ORM來完成,這是一個數據庫通訊的抽象層。

下一步是將我們生成的遷移應用到資料庫:

python manage.py migrate

輸出應該像下面這樣:

Operations to perform:
  Apply all migrations: admin, auth, boards, contenttypes, sessions
Running 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 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 boards.0001_initial... OK
  Applying sessions.0001_initial... OK

因為這是我們第一次遷移資料庫,migrate命令也應用在INSTALLED_APPS中列出的Django開發應用程式現有的遷移檔案。這是預期。

boards.0001_initial...OK是我們在前一步中生成的遷移。

這裡寫圖片描述

  • “但是……什麼資料庫?”你可能會問自己
  • python附帶SQLite — 一個嵌入式的資料庫引擎
  • 它不適合生產。但是我們可以在開發過程中使用它

注意:需要注意的是,SQLite是一個生產質量的資料庫。SQLite被許多公司使用,比如所有的Android和iOS裝置,所有主要的網路瀏覽器,Windows 10,macOS等。
它不適合所有的情況。SQLite不與MySQL、PostgreSQL或Oracle等資料庫進行比較。大容量的網站、寫密集型應用程式、非常大的資料集、高併發性,這些都是使用SQLite最終會導致問題的一些情況。
在專案開發過程中,我們將使用SQLite,因為它很方便,我們不需要安裝其他任何東西。當我們將專案部署到生產環境時,我們將切換到PostgreSQL。對於簡單的網站來說,這樣就很好。但是對於複雜的網站,建議使用相同的資料庫進行開發和生產。

對模型API進行試驗

使用Python開發的最大優點之一是互動式shell。我一直在用它。這是一種快速嘗試的方法,可以嘗試庫和API。

你可以使用manage.py工具來載入我們的專案用以啟動Python shell。

python manage.py shell

這非常類似於通過輸入python來呼叫互動式控制檯,除非我們使用python manage.py shell,我們將專案新增到sys.path並且載入Django。這意味著我們可以匯入我們的模型和專案中的任何其他資源並使用它。

讓我們開始匯入Board類吧:

from boards.models import Board

建立一個board物件,我們可以這樣做:

board = Board(name='Django', description='This is a board about Django.')

要將這個物件持久化到資料庫中,我們必須呼叫save方法:

board.save()

save方法用於建立和更新物件。Django建立了一個新物件,因為Board例項沒有id,在第一次儲存它之後,Django將自動設定該id:

board.id
1

您可以使用Python屬性來訪問其餘的欄位:

board.name
'Django'
board.description
'This is a board about Django.'

更新值,我們可以這樣做:

board.descritption = 'Django discussion board.'
board.save()

每個Django模型都有一個特殊的屬性;我們稱它為模型管理器。您可以通過Python屬性objects訪問它。它主要用於在資料庫中執行查詢。例如,我們可以使用它直接建立一個新的Board物件:

board = Board.objects.create(name='Python', description='General discussion about Python.')
board.id
2
board.name
'Python'

現在,我沒有兩個board。我們可以使用objects來列出資料庫中的所有現有的board:

Board.objects.all()
<QuerySet [<Board: Board object (1)>, <Board: Board object (2)>]>

結果是一個QuerySet。我們以後會學到更多。基本上,它是來自資料庫的物件列表。我們可以看到,我們有兩個物件,但我們只能讀取Board object。這是因為我們還沒有在Board模型中定義__str__方法。

__str__方法是一個物件的字串表示。我們可以用版塊名字來表示它。

首先,退出互動式控制檯:

exit()

models.py檔案的boardsapp中編輯:

class Board(models.Model):
    name = models.CharField(max_length=30, unique=True)
    description = models.CharField(max_length=100)

    def __str__(self):
        return self.name

讓我們再試一次。再次開啟互動控制檯:

python manage.py shell
from boards.models import Board

Board.object.all()
<QuerySet [<Board: Django>, <Board: Python>]>

更好了,是嗎?

我們可以把這個QuerySet像一個列表一樣對待。假設我們想對它進行迭代並列印每個版塊的表述:

boards_list = Board.objects.all()
for board in boards_list:
    print(board.description)

結果將是:

Django discussion board.
General discussion about Python.

類似的,我們可以使用模型管理器查詢資料庫並返回一個物件。為此我們使用了get方法:

django_board = Board.objects.get(id=1)

django_board.name
'Django'

但是我們要小心類似的操作。如果我們獲取一個不存在的物件,例如,id=3的版塊,它將丟擲異常:

board = Board.objects.get(id=3)

boards.models.DoesNotExist: Board matching query does not exist.

我們可以在任何模型欄位使用get方法,但最好使用能夠唯一標識物件的欄位。否則,查詢可能返回多個物件,這將導致異常。

Board.objects.get(name='Django')
<Board: Django>

注意,查詢是大小寫敏感的,小寫的“django”不匹配:

Board.objects.get(name='django')
boards.models.DoesNotExist: Board matching query does not exist.
總結模型操作

以下是我們在本節中學習的方法和操作的總結,使用Board模型作為參考。大寫的Board指的是類,小寫的board指的是Board模型的一個例項(或物件):

board = Board():建立一個沒有儲存的物件
board.save():儲存一個物件(建立或更新)
Board.objects.create(name='...', description='...'):在資料庫中建立並儲存一個物件
Board.objects.all():列出所有的物件
Board.objects.get(id=1):由欄位標識,獲取單個物件

下一節,我們將開始編寫檢視並在HTML頁面中顯示我們的板塊。

檢視、模板和靜態檔案

目前,我們已經有了一個名為home的,在應用程式首頁展示“Hello, World!”的檢視。

myproject/urls.py

from django.conf.urls import url
from django.contrib import admin

from boards import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^admin/', admin.site.urls),
]

boards/views.py

from django.http import HttpResponse

def home(request):
    return HtpResponse('Hello, World!')

我們可以用這個作為起始點。如果你還記得我們的線框圖,它顯示了主頁應該是怎樣的。我們要做的是在一個表中顯示一個列表,並提供一些其他資訊。

首先要做的是匯入Board模型,列出所有現有的版塊:

boards/views.py

from django.http import HttpResponse
from .models import Board

def home(request):
    boards = Board.objects.all()
    boards_names = list()

    for board in boards:
        boards_names.append(board.name)

    response_html = '<br>'.join(boards_names)

    return HttpResponse(response_html)

結果是一個簡單的HTML頁面:
這裡寫圖片描述

讓我們在這裡停一下。我們不會像這樣渲染HTML。對於這個簡單的檢視,我們需要的只是一個版塊列表,然後呈現部分是Django模板引擎的工作。

Django模板引擎設定

在boards和myproject資料夾旁邊,建立一個名為templates的新資料夾

myproject/
 |-- myproject/
 |    |-- boards/
 |    |-- myproject/
 |    |-- templates/   <-- here!
 |    +-- manage.py
 +-- venv/

templates資料夾中,建立一個名為home.html的HTML檔案:

templates/home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Boards</title>
</head>
<body>
    <h1>Boards</h1>

    {% for board in boards %}
        {{ board.name }} <br>
    {% endfor %}

</body>
</html>

在上面的例子中,我們將原始HTML和一些特殊的標籤{% for ... in ... %}{{ variable }}混合在一起。它們是Django模板語言的一部分。上面的例子展示瞭如何使用for迴圈遍歷物件列表。在HTML模板中,{{ board.name }}顯示了版塊的名稱,生成了動態HTML文件。

在我們使用這個HTML頁面之前,我們必須告訴Django在哪裡找到我們的應用程式的模板。

myproject資料夾中開啟settings.py,找到TEMPLATES變數,並將DIRS鍵設定為os.path.join(BASE_DIR, 'templates')

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates')
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

基本上,這一行所做的就是找到您的專案目錄的完整路徑,並將“/templates”附加到它上面。

我們可以使用Python shell對其進行除錯:

python manage.py shell
from django.conf import settings

settings.BASE_DIR
'/Users/vitorfs/Development/myproject'

import os

os.path.join(settings.BASE_DIR, 'templates')
'/Users/vitorfs/Development/myproject/templates'

看到了嗎?它只是指向我們在前面步驟中建立的templates資料夾。

現在,我們可以更新我們的home檢視:

boards/views.py

from django.shortcuts import render
from .models import Board

def home(request):
    boards = Board.objects.all()
    return render(request, 'home.html', {'boards': boards})

HTML結果:
這裡寫圖片描述

我們可以使用表格來改進HTML模板:

templates/home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Boards</title>
</head>
<body>
    <h1>Boards</h1>

    <table border="1">
        <thead>
        <tr>
            <th>Board</th>
            <th>Posts</th>
            <th>Topics</th>
            <th>Last Post</th>
        </tr>
        </thead>
        <tbody>
        {% for board in boards %}
        <tr>
            <td>
                {{ board.name }}<br>
                <small style="color: #888">{{ board.description }}</small>
            </td>
            <td>0</td>
            <td>0</td>
            <td></td>
        </tr>
        {% endfor %}
        </tbody>
    </table>

</body>
</html>

這裡寫圖片描述

測試首頁

這裡寫圖片描述

  • 測試是web開發的一個基本部分。
  • 不管專案有多簡單或多複雜,或者開發人員多老練。
  • 每個專案都應該進行自動化測試。它將幫助我們提高自信並提供更高質量的產品。

這將是一個反覆出現的課題,我們將在整個教程系列中探索不同的概念和策略。

讓我們寫第一個測試。目前,我們將在board應用程式的tests.py檔案中工作:

boards/tests.py

from django.core.urlresolvers import reverse
# django2.x版本使用以下匯入
# from django.urls import reverse
from django.test import TestCase

class HomeTests(TestCase):
    def test_home_view_status_code(self):
        url = reverse('home')
        response = self.client.get(url)
        self.assertEquals(response.status_code, 200)

這是一個非常簡單的測試用例,但非常有用。我們正在測試響應的狀態碼。狀態碼200表示成功。

我們可以在控制檯中檢查響應的狀態碼:
這裡寫圖片描述

如果有一個未捕獲的異常、語法錯誤或其他任何東西,Django將返回狀態碼500,這意味著內部伺服器錯誤。現在,假設我們的應用程式有100個檢視。如果我們為所有檢視編寫這個簡單的測試,只需一個命令,我們就能夠測試所有檢視是否返回成功程式碼,因此使用者在任何地方都看不到任何錯誤訊息。如果沒有自動化測試,我們將需要一個一個地檢查每個頁面。

執行Django的測試套件:

python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.041s

OK
Destroying test database for alias 'default'...

現在我們可以測試Django是否為所請求的URL返回了正確的檢視函式。這也是一個有用的測試,因為當我們在開發過程中取得進展時,您將看到url.py模組可以變得非常大和複雜。URL conf全都是解析正則表示式。在某些情況下,我們有一個非常寬鬆的URL,因此Django可以返回錯誤的檢視函式。

我們是這樣做的:

boards/tests.py

# Django2.x 版本這樣匯入
from django.urls import reverse, resolve
from django.test import TestCase
from .views import home

class HomeTests(TestCase):
    def test_home_view_status_code(self):
        url = reverse('home')
        response = self.client.get(url)
        self.assertEquals(response.status_code, 200)

    def test_home_url_resolves_home_view(self):
        view = reverse('/')
        self.assertEquals(view.func, home)

在第二個測試中,我們使用了resolve函式。Django用它來將urls.py模組中的URL列表和請求的URL相匹配。這個測試將確保URL\,即根URL,返回home檢視。

再一次測試:

python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.027s

OK
Destroying test database for alias 'default'...

要檢視更多關於測試執行的細節,請將其設定為更高的級別:

python manage.py test --verbosity=2
Creating test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
Operations to perform:
  Synchronize unmigrated apps: messages, staticfiles
  Apply all migrations: admin, auth, boards, contenttypes, sessions
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
Running 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 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 boards.0001_initial... OK
  Applying sessions.0001_initial... OK
System check identified no issues (0 silenced).
test_home_url_resolves_home_view (boards.tests.HomeTests) ... ok
test_home_view_status_code (boards.tests.HomeTests) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.017s

OK
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...

Verbosity決定將列印到控制檯的通知和除錯資訊的數量;0不是輸出,1是正常輸出,2是冗長輸出。

靜態檔案設定

靜態檔案是CSS、javascript、字型、影象,或者我們可以用來組成使用者介面的任何其他資源。

實際上,Django並不提供這些檔案。除了在開發過程中,讓我們的生活更輕鬆。但是Django提供了一些特性來幫助我們管理靜態檔案。這些特性在已經在INSTALLED_APPS配置中列出的django.contrib.staticfiles 應用程式中可用。

有了這麼多的前端元件庫,我們沒有理由繼續呈現基本的HTML文件。我們可以很容易地在我們的專案中新增Bootstrap 4。Bootstrap是一個用於開發HTML、CSS和JavaScript的開源工具包。

在專案根目錄下,連boards、templates和myproject資料夾一起,建立一個名為static的新資料夾,在static資料夾中建立另一個命名為css的資料夾:

myproject/
 |-- myproject/
 |    |-- boards/
 |    |-- myproject/
 |    |-- templates/
 |    |-- static/       <-- here
 |    |    +-- css/     <-- and here
 |    +-- manage.py
 +-- venv/

下載Compiled CSS and JS。

在你的計算機中,解壓你從網站中下載的壓縮包,複製css/bootstrap.min.css到你的專案的css資料夾中。

myproject/
 |-- myproject/
 |    |-- boards/
 |    |-- myproject/
 |    |-- templates/
 |    |-- static/
 |    |    +-- css/
 |    |         +-- bootstrap.min.css    <-- here
 |    +-- manage.py
 +-- venv/

下一步是指導Django在哪裡找到靜態檔案。開啟settings.py,滾動到檔案底部,然後在STATIC_URL後新增以下內容:

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

TEMPLATES目錄相同,記得嗎?

現在,我們必須在模板中載入靜態檔案(引導CSS檔案):

templates/home.html

{% load static %}<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Boards</title>
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  </head>
  <body>
    <!-- body suppressed for brevity ... -->
  </body>
</html>

首先,我們使用模板開頭的{% load static %}來載入靜態檔案。

模板標記{% static %}用於構成資源所在的URL。在這種情況下,{% static 'css/bootstrap.min.css' %}將返回/static/css/bootstrap.min.css,相當於。

{% static %}模板標記使用settings.pySTATIC_URL配置來組成最終的URL。如果您將靜態檔案託管在子域中