Django 教程第三部分:使用模型

本文展示瞭如何為 LocalLibrary 網站定義模型。它解釋了什麼是模型、如何宣告模型以及一些主要的欄位型別。它還簡要介紹了訪問模型資料的一些主要方法。

預備知識 Django 教程第二部分:建立一個骨架網站.
目標

能夠設計和建立自己的模型,並選擇合適的欄位。

概述

Django Web 應用程式透過稱為模型的 Python 物件訪問和管理資料。模型定義了儲存資料的結構,包括欄位型別,也可能包括其最大大小、預設值、選擇列表選項、文件的幫助文字、表單的標籤文字等。模型的定義獨立於底層資料庫——你可以在專案設定中選擇其中一個。一旦你選擇了要使用的資料庫,你根本不需要直接與它對話——你只需編寫模型結構和其他程式碼,Django 會為你處理所有與資料庫通訊的繁瑣工作。

本教程展示瞭如何定義和訪問 LocalLibrary 網站示例的模型。

設計 LocalLibrary 模型

在你開始編寫模型程式碼之前,值得花幾分鐘時間思考我們需要儲存哪些資料以及不同物件之間的關係。

我們知道需要儲存書籍資訊(標題、摘要、作者、書寫語言、類別、ISBN),並且可能有多本可用的副本(具有全域性唯一 ID、可用狀態等)。我們可能需要儲存比作者姓名更多的作者資訊,並且可能存在姓名相同或相似的多個作者。我們希望能夠根據書名、作者、書寫語言和類別對資訊進行排序。

在設計模型時,為每個“物件”(一組相關資訊)設定單獨的模型是有意義的。在這種情況下,明顯的物件是書籍、書籍例項和作者。

你可能還希望使用模型來表示選擇列表選項(例如,像下拉選擇列表),而不是將選擇項硬編碼到網站本身——當並非所有選項都事先已知或可能會更改時,建議這樣做。在這種情況下,明顯的模型候選物件包括書籍型別(例如,科幻小說、法國詩歌等)和語言(英語、法語、日語)。

一旦我們確定了模型和欄位,我們需要考慮關係。Django 允許你定義一對一 (OneToOneField)、一對多 (ForeignKey) 和多對多 (ManyToManyField) 關係。

考慮到這一點,下面的 UML 關聯圖顯示了我們將在此案例中定義的模型(作為框)。

LocalLibrary Model UML with fixed Author multiplicity inside the Book class

我們建立了書籍(書籍的通用詳細資訊)、書籍例項(系統中可用書籍特定物理副本的狀態)和作者的模型。我們還決定為型別設定一個模型,以便可以透過管理介面建立/選擇值。我們決定不為 BookInstance:status 設定模型——我們硬編碼了這些值 (LOAN_STATUS),因為我們預計它們不會改變。在每個框中,你可以看到模型名稱、欄位名稱和型別,以及方法及其返回型別。

該圖還顯示了模型之間的關係,包括它們的多重性。多重性是圖上的數字,顯示了關係中可能存在的每個模型的數量(最大值和最小值)。例如,框之間的連線線顯示書籍和型別是相關的。靠近型別模型的數字顯示一本書必須有一個或多個型別(你喜歡的任意數量),而靠近書籍模型另一端的數字顯示一個型別可以有零個或多個關聯書籍。

注意:下一節提供了一個基本入門,解釋瞭如何定義和使用模型。在閱讀時,請考慮我們將如何構建上面圖中的每個模型。

模型基礎知識

本節簡要概述瞭如何定義模型以及一些更重要的欄位和欄位引數。

模型定義

模型通常定義在應用程式的 models.py 檔案中。它們作為 django.db.models.Model 的子類實現,可以包含欄位、方法和元資料。下面的程式碼片段顯示了一個名為 MyModelName 的“典型”模型。

python
from django.db import models
from django.urls import reverse

class MyModelName(models.Model):
    """A typical class defining a model, derived from the Model class."""

    # Fields
    my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
    # …

    # Metadata
    class Meta:
        ordering = ['-my_field_name']

    # Methods
    def get_absolute_url(self):
        """Returns the URL to access a particular instance of MyModelName."""
        return reverse('model-detail-view', args=[str(self.id)])

    def __str__(self):
        """String for representing the MyModelName object (in Admin site etc.)."""
        return self.my_field_name

在以下部分中,我們將詳細探討模型中的每個特性。

欄位

模型可以有任意數量的任意型別的欄位——每個欄位代表我們希望儲存在資料庫表中的一列資料。每個資料庫記錄(行)將包含每個欄位的一個值。讓我們看看下面的例子。

python
my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')

我們上面的示例有一個名為 my_field_name 的欄位,型別為 models.CharField——這意味著該欄位將包含字母數字字串。欄位型別使用特定的類進行分配,這些類決定了用於在資料庫中儲存資料記錄的型別,以及從 HTML 表單接收值時要使用的驗證標準(即,什麼構成有效值)。欄位型別還可以採用進一步指定欄位儲存或使用方式的引數。在這種情況下,我們為欄位提供了兩個引數。

  • max_length=20 — 表示此欄位值的最大長度為 20 個字元。
  • help_text='Enter field documentation' — 在表單中可能顯示的有用文字,幫助使用者理解欄位的用途。

欄位名用於在查詢和模板中引用它。欄位也有一個標籤,它使用 verbose_name 引數指定(預設值為 None)。如果未設定 verbose_name,則透過將下劃線替換為空格並將第一個字母大寫來從欄位名建立標籤(例如,欄位 my_field_name 在表單中使用時將具有預設標籤 My field name)。

如果模型在表單中呈現(例如,在管理站點中),欄位的宣告順序將影響其預設順序,儘管這可能會被覆蓋。

常用欄位引數

在宣告許多/大多數不同欄位型別時,可以使用以下常用引數:

  • help_text: 為 HTML 表單提供文字標籤(例如,在管理站點中),如上所述。

  • verbose_name: 欄位的易讀名稱,用於欄位標籤。如果未指定,Django 將根據欄位名推斷預設的 verbose name。

  • default: 欄位的預設值。這可以是一個值或一個可呼叫物件,在這種情況下,每次建立新記錄時都會呼叫該物件。

  • null: 如果為 True,Django 會將空白值作為 NULL 儲存在資料庫中,適用於此欄位型別(CharField 會儲存空字串)。預設值為 False

  • blank: 如果為 True,則欄位允許在表單中為空。預設值為 False,這意味著 Django 的表單驗證將強制你輸入一個值。這通常與 null=True 一起使用,因為如果你允許空白值,你也會希望資料庫能夠適當地表示它們。

  • choices: 此欄位的一組選擇。如果提供此引數,則預設的相應表單小部件將是一個帶有這些選擇的下拉框,而不是標準的文字欄位。

  • unique: 如果為 True,則確保欄位值在整個資料庫中是唯一的。這可用於防止欄位值重複。預設值為 False

  • primary_key: 如果為 True,則將當前欄位設定為模型的主鍵(主鍵是用於唯一標識所有不同表記錄的特殊資料庫列)。如果未指定任何欄位作為主鍵,Django 將自動為此目的新增一個欄位。自動建立的主鍵欄位的型別可以在 AppConfig.default_auto_field 中為每個應用程式指定,或在 DEFAULT_AUTO_FIELD 設定中全域性指定。

    注意:使用 manage.py 建立的應用程式將主鍵型別設定為 BigAutoField。你可以在本地庫 catalog/apps.py 檔案中看到這一點。

    python
    class CatalogConfig(AppConfig):
      default_auto_field = 'django.db.models.BigAutoField'
    

還有許多其他選項——你可以在此處檢視 欄位選項的完整列表

常用欄位型別

以下列表描述了一些更常用的欄位型別。

  • CharField 用於定義短到中等長度的固定長度字串。你必須指定要儲存資料的 max_length
  • TextField 用於大型任意長度字串。你可以為欄位指定 max_length,但這僅在欄位在表單中顯示時使用(在資料庫級別不強制執行)。
  • IntegerField 是一個用於儲存整數(整數)值,並在表單中將輸入值驗證為整數的欄位。
  • DateFieldDateTimeField 用於儲存/表示日期和日期/時間資訊(分別作為 Python datetime.datedatetime.datetime 物件)。這些欄位可以額外宣告(互斥)引數 auto_now=True(在每次儲存模型時將欄位設定為當前日期)、auto_now_add(僅在首次建立模型時設定日期)和 default(設定一個可以被使用者覆蓋的預設日期)。
  • EmailField 用於儲存和驗證電子郵件地址。
  • FileFieldImageField 分別用於上傳檔案和影像(ImageField 添加了額外的驗證,即上傳的檔案是影像)。這些欄位具有定義上傳檔案儲存方式和位置的引數。
  • AutoField 是一種特殊型別的 IntegerField,它會自動遞增。如果你沒有明確指定主鍵,則會自動向你的模型新增此型別的主鍵。
  • ForeignKey 用於指定與另一個數據庫模型的一對多關係(例如,一輛汽車有一個製造商,但一個製造商可以生產許多汽車)。關係中的“一”方是包含“鍵”的模型(包含引用該“鍵”的“外部索引鍵”的模型,處於此類關係的“多”方)。
  • ManyToManyField 用於指定多對多關係(例如,一本書可以有多種型別,每種型別可以包含多本書)。在我們的圖書館應用程式中,我們將非常類似於 ForeignKeys 使用它們,但它們可以以更復雜的方式用於描述組之間的關係。這些欄位具有 on_delete 引數,用於定義關聯記錄被刪除時會發生什麼(例如,models.SET_NULL 的值會將值設定為 NULL)。

還有許多其他型別的欄位,包括用於不同型別數字(大整數、小整數、浮點數)、布林值、URL、slug、唯一 ID 和其他“時間相關”資訊(持續時間、時間等)的欄位。你可以在此處檢視 完整列表

元資料

你可以透過宣告 class Meta 來為你的模型宣告模型級元資料,如下所示。

python
class Meta:
    ordering = ['-my_field_name']

此元資料最有用的功能之一是控制查詢模型型別時返回記錄的預設排序。你透過在 ordering 屬性的欄位名稱列表中指定匹配順序來完成此操作,如上所示。排序將取決於欄位型別(字元欄位按字母順序排序,而日期欄位按時間順序排序)。如上所示,你可以在欄位名稱前加上減號 (-) 以反轉排序順序。

因此,舉例來說,如果我們選擇預設按如下方式對書籍進行排序:

python
ordering = ['title', '-publish_date']

書籍將按標題從 A 到 Z 按字母順序排序,然後每個標題內部按出版日期從新到舊排序。

另一個常用屬性是 verbose_name,它是類的單數和複數形式的詳細名稱。

python
verbose_name = 'BetterName'

類元資料可用於建立和應用模型的新“訪問許可權”(預設許可權會自動應用),允許基於另一個欄位進行排序,定義可儲存資料可能值的約束,或宣告該類是“抽象”的(一個你無法為其建立記錄的基礎類,而是從中派生以建立其他模型)。

許多其他元資料選項控制必須用於模型的資料庫以及資料如何儲存(這些選項只有在你需要將模型對映到現有資料庫時才真正有用)。

元資料選項的完整列表可在以下位置獲取:模型元資料選項 (Django docs)。

方法

模型還可以有方法。

至少,在每個模型中,你應該定義標準的 Python 類方法 __str__(),以便為每個物件返回一個人類可讀的字串。此字串用於在管理站點(以及你需要引用模型例項的任何其他地方)中表示單個記錄。通常,它將返回模型中的標題或名稱欄位。

python
def __str__(self):
    return self.my_field_name

Django 模型中包含的另一個常用方法是 get_absolute_url(),它返回一個用於在網站上顯示單個模型記錄的 URL(如果你定義了此方法,Django 將自動在管理站點中模型的記錄編輯螢幕上新增一個“在站點上檢視”按鈕)。下面顯示了 get_absolute_url() 的典型模式。

python
def get_absolute_url(self):
    """Returns the URL to access a particular instance of the model."""
    return reverse('model-detail-view', args=[str(self.id)])

注意:假設你將使用類似 /my-application/my-model-name/2 的 URL 來顯示模型的單個記錄(其中“2”是特定記錄的 id),你將需要建立一個 URL 對映器來將響應和 id 傳遞給一個“模型詳細檢視”(它將完成顯示記錄所需的工作)。上面的 reverse() 函式能夠“反轉”你的 URL 對映器(在上面的例子中名為 'model-detail-view'),以建立正確格式的 URL。

當然,要使其工作,你仍然必須編寫 URL 對映、檢視和模板!

你還可以定義任何其他你喜歡的方法,並從你的程式碼或模板中呼叫它們(前提是它們不帶任何引數)。

模型管理

一旦你定義了模型類,就可以使用它們來建立、更新或刪除記錄,並執行查詢以獲取所有記錄或特定記錄子集。我們將在教程中定義檢視時向你展示如何操作,但這裡有一個簡要總結。

建立和修改記錄

要建立記錄,你可以定義模型的一個例項,然後呼叫 save()

python
# Create a new record using the model's constructor.
record = MyModelName(my_field_name="Instance #1")

# Save the object into the database.
record.save()

注意:如果你沒有宣告任何欄位為 primary_key,新記錄將自動獲得一個,欄位名為 id。你可以在儲存上述記錄後查詢此欄位,其值將為 1。

你可以使用點語法訪問此新記錄中的欄位,並更改值。你必須呼叫 save() 才能將修改後的值儲存到資料庫中。

python
# Access model field values using Python attributes.
print(record.id) # should return 1 for the first record.
print(record.my_field_name) # should print 'Instance #1'

# Change record by modifying the fields, then calling save().
record.my_field_name = "New Instance Name"
record.save()

搜尋記錄

你可以使用模型的 objects 屬性(由基類提供)搜尋符合特定條件的記錄。

注意:解釋如何使用“抽象”模型和欄位名搜尋記錄可能會有點令人困惑。在下面的討論中,我們將引用一個帶有 titlegenre 欄位的 Book 模型,其中 genre 也是一個只有一個 name 欄位的模型。

我們可以使用 objects.all() 獲取模型的所有記錄,作為 QuerySetQuerySet 是一個可迭代物件,這意味著它包含許多我們可以迭代/遍歷的物件。

python
all_books = Book.objects.all()

Django 的 filter() 方法允許我們過濾返回的 QuerySet,以使指定的文字數字欄位匹配特定條件。例如,要篩選標題中包含“wild”的書籍,然後統計它們,我們可以這樣做:

python
wild_books = Book.objects.filter(title__contains='wild')
number_wild_books = wild_books.count()

要匹配的欄位和匹配型別在過濾器引數名稱中定義,使用格式:field_name__match_type(請注意上面 titlecontains 之間的雙下劃線)。上面我們正在使用區分大小寫的匹配來過濾 title。你可以進行許多其他型別的匹配:icontains(不區分大小寫)、iexact(不區分大小寫的精確匹配)、exact(區分大小寫的精確匹配)以及 ingt(大於)、startswith 等等。完整列表在這裡

在某些情況下,你需要根據定義與其他模型一對多關係(例如,ForeignKey)的欄位進行過濾。在這種情況下,你可以使用額外的雙下劃線“索引”到相關模型中的欄位。因此,例如,要過濾具有特定型別模式的書籍,你必須透過 genre 欄位索引到 name,如下所示:

python
# Will match on: Fiction, Science fiction, non-fiction etc.
books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')

注意:你可以使用下劃線(__)導航任意多層關係(ForeignKey/ManyToManyField)。例如,一本具有不同型別的 Book,透過進一步的“cover”關係定義,可能具有一個引數名稱:type__cover__name__exact='hard'

查詢還可以做更多事情,包括從相關模型進行反向搜尋、連結過濾器、返回更小的值集等。有關更多資訊,請參閱進行查詢(Django 文件)。

定義 LocalLibrary 模型

在本節中,我們將開始定義圖書館的模型。開啟 models.py (在 /django-locallibrary-tutorial/catalog/)。頁面頂部的樣板程式碼匯入了 models 模組,其中包含我們的模型將繼承的基類 models.Model

python
from django.db import models

# Create your models here.

型別模型

複製下面所示的 Genre 模型程式碼,並將其貼上到 models.py 檔案的底部。此模型用於儲存有關書籍類別的資訊——例如,它是小說還是非小說,浪漫還是軍事歷史等。如上所述,我們將型別建立為模型,而不是自由文字或選擇列表,以便可以透過資料庫而不是硬編碼來管理可能的值。

python
from django.urls import reverse # Used in get_absolute_url() to get URL for specified ID

from django.db.models import UniqueConstraint # Constrains fields to unique values
from django.db.models.functions import Lower # Returns lower cased value of field

class Genre(models.Model):
    """Model representing a book genre."""
    name = models.CharField(
        max_length=200,
        unique=True,
        help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
    )

    def __str__(self):
        """String for representing the Model object."""
        return self.name

    def get_absolute_url(self):
        """Returns the url to access a particular genre instance."""
        return reverse('genre-detail', args=[str(self.id)])

    class Meta:
        constraints = [
            UniqueConstraint(
                Lower('name'),
                name='genre_name_case_insensitive_unique',
                violation_error_message = "Genre already exists (case insensitive match)"
            ),
        ]

該模型有一個 CharField 欄位 (name),用於描述型別(限制為 200 個字元並帶有一些 help_text)。我們將此欄位設定為唯一 (unique=True),因為每個型別應該只有一個記錄。

在欄位之後,我們聲明瞭一個 __str__() 方法,它返回特定記錄定義的型別的名稱。沒有定義詳細名稱,因此當在表單中使用時,欄位標籤將是 Name。然後我們宣告 get_absolute_url() 方法,它返回一個 URL,可用於訪問此模型的詳細記錄(為此,我們將不得不定義一個名為 genre-detail 的 URL 對映,並定義一個關聯的檢視和模板)。

上面欄位上的 unique=True 設定可以防止建立名稱完全相同的型別,但不能防止“fantasy”、“Fantasy”甚至“FaNtAsY”等變體。模型定義的最後一部分使用模型元資料上的 constraints 選項來指定 name 欄位中的小寫值在資料庫中必須是唯一的,如果不是,則顯示 violation_error_message 字串。這裡我們不需要做其他任何事情,但是你可以針對一個或多個欄位定義多個約束。有關更多資訊,請參閱約束參考,包括 UniqueConstraint()(和 Lower())。

書籍模型

複製下面的 Book 模型,然後再次將其貼上到檔案的底部。Book 模型以通用意義表示所有關於可用書籍的資訊,而不是可供借閱的特定物理“例項”或“副本”。

該模型使用 CharField 來表示書籍的 titleisbn。對於 isbn,請注意第一個未命名引數如何明確將標籤設定為“ISBN”(否則,它將預設為“Isbn”)。我們還將引數 unique 設定為 true,以確保所有書籍都具有唯一的 ISBN(unique 引數使欄位值在表中全域性唯一)。與 isbn(和型別名稱)不同,title 未設定為唯一,因為不同的書籍可能具有相同的名稱。該模型使用 TextField 作為 summary,因為此文字可能需要很長。

python
class Book(models.Model):
    """Model representing a book (but not a specific copy of a book)."""
    title = models.CharField(max_length=200)
    author = models.ForeignKey('Author', on_delete=models.RESTRICT, null=True)
    # Foreign Key used because book can only have one author, but authors can have multiple books.
    # Author as a string rather than object because it hasn't been declared yet in file.

    summary = models.TextField(
        max_length=1000, help_text="Enter a brief description of the book")
    isbn = models.CharField('ISBN', max_length=13,
                            unique=True,
                            help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn'
                                      '">ISBN number</a>')

    # ManyToManyField used because genre can contain many books. Books can cover many genres.
    # Genre class has already been defined so we can specify the object above.
    genre = models.ManyToManyField(
        Genre, help_text="Select a genre for this book")

    def __str__(self):
        """String for representing the Model object."""
        return self.title

    def get_absolute_url(self):
        """Returns the URL to access a detail record for this book."""
        return reverse('book-detail', args=[str(self.id)])

型別是一個 ManyToManyField,因此一本書可以有多種型別,一個型別可以包含多本書。作者被宣告為 ForeignKey,所以每本書只有一個作者,但一個作者可以有許多書(實際上,一本書可能有多個作者,但在此實現中沒有!)。

在這兩種欄位型別中,關聯的模型類被宣告為第一個未命名引數,使用模型類或包含關聯模型名稱的字串。如果關聯類在引用之前尚未在此檔案中定義,則必須使用模型名稱作為字串!author 欄位中其他感興趣的引數是 null=True,它允許資料庫在未選擇作者時儲存 Null 值,以及 on_delete=models.RESTRICT,它將阻止刪除書籍的關聯作者(如果它被任何書籍引用)。

警告:預設情況下 on_delete=models.CASCADE,這意味著如果作者被刪除,這本書也會被刪除!我們這裡使用 RESTRICT,但我們也可以使用 PROTECT 來防止作者在使用它的任何書籍時被刪除,或者使用 SET_NULL 來在記錄被刪除時將書籍的作者設定為 Null

該模型還定義了 __str__(),使用書籍的 title 欄位來表示 Book 記錄。最後一個方法 get_absolute_url() 返回一個 URL,可用於訪問此模型的詳細記錄(我們將不得不定義一個名為 book-detail 的 URL 對映,並定義一個關聯的檢視和模板)。

書籍例項模型

接下來,將 BookInstance 模型(如下所示)複製到其他模型下方。BookInstance 表示一本書的特定副本,某人可能會借閱,幷包含有關副本是否可用、預計歸還日期、“印記”或版本詳細資訊,以及圖書館中該書的唯一 ID 的資訊。

現在,有些欄位和方法會讓你感到熟悉。該模型使用:

  • ForeignKey 來標識關聯的 Book(每本書可以有許多副本,但一個副本只能有一個 Book)。該鍵指定 on_delete=models.RESTRICT 以確保在被 BookInstance 引用時不能刪除 Book
  • CharField 來表示書籍的印記(特定版本)。
python
import uuid # Required for unique book instances

class BookInstance(models.Model):

    """Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4,
                          help_text="Unique ID for this particular book across whole library")
    book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
    imprint = models.CharField(max_length=200)
    due_back = models.DateField(null=True, blank=True)

    LOAN_STATUS = (
        ('m', 'Maintenance'),
        ('o', 'On loan'),
        ('a', 'Available'),
        ('r', 'Reserved'),
    )

    status = models.CharField(
        max_length=1,
        choices=LOAN_STATUS,
        blank=True,
        default='m',
        help_text='Book availability',
    )

    class Meta:
        ordering = ['due_back']

    def __str__(self):
        """String for representing the Model object."""
        return f'{self.id} ({self.book.title})'

我們額外聲明瞭幾種新型別的欄位:

  • UUIDField 用於 id 欄位,將其設定為此模型的 primary_key。這種型別的欄位為每個例項分配一個全域性唯一值(圖書館中每本你可以找到的書籍一個)。
  • DateField 用於 due_back 日期(借出或維護後預計可用的日期)。此值可以為 blanknull(當書籍可用時需要)。模型元資料(Class Meta)使用此欄位在查詢中返回記錄時進行排序。
  • status 是一個 CharField,它定義了一個選擇/選擇列表。如你所見,我們定義了一個包含鍵值對元組的元組,並將其傳遞給 choices 引數。鍵/值對中的值是使用者可以選擇的顯示值,而鍵是如果選擇了該選項實際儲存的值。我們還設定了預設值 'm'(維護),因為書籍最初在上架之前將被建立為不可用。

方法 __str__() 使用其唯一 ID 和關聯 Book 的標題的組合來表示 BookInstance 物件。

注意:一點 Python

  • 從 Python 3.6 開始,你可以使用字串插值語法(也稱為 f-strings):f'{self.id} ({self.book.title})'
  • 在本教程的舊版本中,我們使用的是 格式化字串 語法,這也是 Python 中格式化字串的有效方法(例如,'{0} ({1})'.format(self.id,self.book.title))。

作者模型

Author 模型(如下所示)複製到 models.py 中現有程式碼的下方。

python
class Author(models.Model):
    """Model representing an author."""
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)

    class Meta:
        ordering = ['last_name', 'first_name']

    def get_absolute_url(self):
        """Returns the URL to access a particular author instance."""
        return reverse('author-detail', args=[str(self.id)])

    def __str__(self):
        """String for representing the Model object."""
        return f'{self.last_name}, {self.first_name}'

現在所有欄位/方法都應該很熟悉了。該模型定義了一個作者,包括名字、姓氏、出生日期和死亡日期(兩者都是可選的)。它指定預設情況下 __str__()姓氏名字的順序返回姓名。get_absolute_url() 方法反轉 author-detail URL 對映以獲取顯示單個作者的 URL。

重新執行資料庫遷移

你的所有模型都已建立。現在重新執行資料庫遷移以將它們新增到你的資料庫中。

bash
python3 manage.py makemigrations
python3 manage.py migrate

語言模型 — 挑戰

想象一位當地的慈善家捐贈了許多用另一種語言(比如說,波斯語)編寫的新書。挑戰在於找出如何在我們的圖書館網站中最好地表示這些書籍,然後將它們新增到模型中。

一些需要考慮的事情:

  • “語言”應該與 BookBookInstance 還是其他物件關聯?
  • 不同的語言應該使用模型、自由文字欄位還是硬編碼選擇列表來表示?

做出決定後,新增欄位。你可以在 GitHub 上的專案中檢視我們是如何決定的。

請記住,模型更改後,應再次重新執行資料庫遷移以新增更改。

bash
python3 manage.py makemigrations
python3 manage.py migrate

總結

在本文中,我們學習瞭如何定義模型,然後利用這些資訊為 LocalLibrary 網站設計並實現了合適的模型。

此時,我們將暫時偏離建立網站,並檢視 Django 管理站點。該站點將允許我們向圖書館新增一些資料,然後我們可以使用我們(尚未建立)的檢視和模板來顯示這些資料。

另見