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

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

先決條件 Django 教程第 2 部分:建立網站框架.
目標

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

概述

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 名稱。
  • 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檔案中看到這一點

    py
    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', '-pubdate']

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

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

python
verbose_name = 'BetterName'

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

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

完整元資料選項列表如下:模型元資料選項(Django 文件)。

方法

模型也可以包含方法。

最少,在每個模型中,您都應該定義標準 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)])

注意: 假設您將使用類似 /myapplication/mymodelname/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 模型,其中型別也是一個具有單個欄位 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 模型

複製下面顯示的 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 欄位中值的 lowercase 必須在資料庫中唯一,並且如果它不唯一,則顯示 violation_error_message 字串。在這裡,我們不需要執行任何其他操作,但您可以針對一個或多個欄位定義多個約束。有關詳細資訊,請參閱 約束參考,包括 UniqueConstraint()(和 Lower())。

Book 模型

複製下面的 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,該 URL 可用於訪問該模型的詳細資訊記錄(我們將必須定義一個名為book-detail的 URL 對映,並定義一個關聯的檢視和模板)。

BookInstance 模型

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

現在,一些欄位和方法應該很熟悉了。該模型使用

  • ForeignKey來識別關聯的Book(每本書可以有多個副本,但一個副本只能有一個Book)。金鑰指定on_delete=models.RESTRICT以確保Book不能在被BookInstance引用時被刪除。
  • 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-字串):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 管理網站。這個網站將允許我們向圖書館新增一些資料,然後我們可以使用我們(尚未建立的)檢視和模板來顯示這些資料。

另請參閱