模型實例參考

本文件描述了 Model API 的詳細資訊。它建立在 模型資料庫查詢 指南中介紹的內容之上,因此您可能需要在閱讀本文之前先閱讀並理解這些文件。

在本參考資料中,我們將使用 資料庫查詢指南 中介紹的範例部落格模型

建立物件

要建立模型的新實例,請像其他 Python 類別一樣實例化它。

class Model(**kwargs)[原始碼]

關鍵字引數是您在模型上定義的欄位名稱。請注意,實例化模型不會觸及您的資料庫;為此,您需要使用 save()

注意

您可能會想透過覆寫 __init__ 方法來自訂模型。但是,如果您這樣做,請注意不要變更呼叫簽名,因為任何變更都可能會阻止儲存模型實例。此外,在 __init__ 中參照模型欄位可能會在某些情況下導致無限遞迴錯誤。不要覆寫 __init__,請嘗試使用下列方法之一:

  1. 在模型類別上新增類別方法

    from django.db import models
    
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        @classmethod
        def create(cls, title):
            book = cls(title=title)
            # do something with the book
            return book
    
    
    book = Book.create("Pride and Prejudice")
    
  2. 在自訂管理員上新增方法 (通常偏好)

    class BookManager(models.Manager):
        def create_book(self, title):
            book = self.create(title=title)
            # do something with the book
            return book
    
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        objects = BookManager()
    
    
    book = Book.objects.create_book("Pride and Prejudice")
    

自訂模型載入

classmethod Model.from_db(db, field_names, values)[原始碼]

當從資料庫載入時,可以使用 from_db() 方法來自訂模型實例的建立。

db 引數包含載入模型的資料庫別名,field_names 包含所有已載入欄位的名稱,而 values 則包含 field_names 中每個欄位的已載入值。field_names 的順序與 values 的順序相同。如果存在所有模型的欄位,則保證 values 的順序是 __init__() 預期的順序。也就是說,可以透過 cls(*values) 建立實例。如果有任何欄位被延遲,它們將不會出現在 field_names 中。在這種情況下,請為每個遺失的欄位指派值 django.db.models.DEFERRED

除了建立新的模型之外,from_db() 方法還必須在新實例的 _state 屬性中設定 addingdb 旗標。

以下範例說明如何記錄從資料庫載入的欄位的初始值。

from django.db.models import DEFERRED


@classmethod
def from_db(cls, db, field_names, values):
    # Default implementation of from_db() (subject to change and could
    # be replaced with super()).
    if len(values) != len(cls._meta.concrete_fields):
        values = list(values)
        values.reverse()
        values = [
            values.pop() if f.attname in field_names else DEFERRED
            for f in cls._meta.concrete_fields
        ]
    instance = cls(*values)
    instance._state.adding = False
    instance._state.db = db
    # customization to store the original field values on the instance
    instance._loaded_values = dict(
        zip(field_names, (value for value in values if value is not DEFERRED))
    )
    return instance


def save(self, **kwargs):
    # Check how the current values differ from ._loaded_values. For example,
    # prevent changing the creator_id of the model. (This example doesn't
    # support cases where 'creator_id' is deferred).
    if not self._state.adding and (
        self.creator_id != self._loaded_values["creator_id"]
    ):
        raise ValueError("Updating the value of creator isn't allowed")
    super().save(**kwargs)

上面的範例顯示了完整的 from_db() 實作,以釐清其完成方式。在這種情況下,可以在 from_db() 方法中使用 super() 呼叫。

從資料庫重新整理物件

如果您從模型實例中刪除一個欄位,再次存取它會從資料庫重新載入該值。

>>> obj = MyModel.objects.first()
>>> del obj.field
>>> obj.field  # Loads the field from the database
Model.refresh_from_db(using=None, fields=None, from_queryset=None)[原始碼]
Model.arefresh_from_db(using=None, fields=None, from_queryset=None)

非同步版本arefresh_from_db()

如果您需要從資料庫重新載入模型的值,可以使用 refresh_from_db() 方法。當呼叫此方法時不帶引數時,會執行以下操作:

  1. 模型的所有非延遲欄位都會更新為目前資料庫中的值。

  2. 所有快取的關聯都會從重新載入的實例中清除。

只有模型的欄位會從資料庫重新載入。其他與資料庫相關的值 (例如註釋) 不會重新載入。任何 @cached_property 屬性也不會清除。

重新載入發生在載入實例的資料庫,如果實例不是從資料庫載入,則從預設資料庫重新載入。可以使用 using 引數來強制用於重新載入的資料庫。

可以使用 fields 引數來強制載入的欄位集。

例如,若要測試 update() 呼叫是否產生預期的更新,您可以撰寫類似於以下的測試:

def test_update_result(self):
    obj = MyModel.objects.create(val=1)
    MyModel.objects.filter(pk=obj.pk).update(val=F("val") + 1)
    # At this point obj.val is still 1, but the value in the database
    # was updated to 2. The object's updated value needs to be reloaded
    # from the database.
    obj.refresh_from_db()
    self.assertEqual(obj.val, 2)

請注意,當存取延遲欄位時,延遲欄位的值的載入會透過此方法進行。因此,可以自訂延遲載入的方式。以下範例說明當重新載入延遲欄位時,如何重新載入實例的所有欄位。

class ExampleModel(models.Model):
    def refresh_from_db(self, using=None, fields=None, **kwargs):
        # fields contains the name of the deferred field to be
        # loaded.
        if fields is not None:
            fields = set(fields)
            deferred_fields = self.get_deferred_fields()
            # If any deferred field is going to be loaded
            if fields.intersection(deferred_fields):
                # then load all of them
                fields = fields.union(deferred_fields)
        super().refresh_from_db(using, fields, **kwargs)

from_queryset 引數允許使用與從 _base_manager 建立的查詢集不同的查詢集。它讓您可以更精確地控制模型的重新載入方式。例如,當您的模型使用軟刪除時,您可以讓 refresh_from_db() 考慮到這一點。

obj.refresh_from_db(from_queryset=MyModel.active_objects.all())

您可以快取相關的物件,否則這些物件會從重新載入的實例中清除。

obj.refresh_from_db(from_queryset=MyModel.objects.select_related("related_field"))

您可以在重新載入模型的值之前,鎖定列直到交易結束。

obj.refresh_from_db(from_queryset=MyModel.objects.select_for_update())
在 Django 5.1 中變更

新增了 from_queryset 引數。

Model.get_deferred_fields()[原始碼]

一個輔助方法,傳回一個集合,其中包含此模型目前延遲的所有欄位的屬性名稱。

驗證物件

驗證模型涉及四個步驟

  1. 驗證模型欄位 - Model.clean_fields()

  2. 將整個模型做驗證 - Model.clean()

  3. 驗證欄位的唯一性 - Model.validate_unique()

  4. 驗證約束 - Model.validate_constraints()

當您呼叫模型的 full_clean() 方法時,會執行所有這四個步驟。

當您使用 ModelForm 時,呼叫 is_valid() 將會對表單中包含的所有欄位執行這些驗證步驟。請參閱 ModelForm 文件 以取得更多資訊。如果您計劃自行處理驗證錯誤,或您已從 ModelForm 中排除需要驗證的欄位,則您只需要呼叫模型的 full_clean() 方法。

Model.full_clean(exclude=None, validate_unique=True, validate_constraints=True)[原始碼]

此方法會依序呼叫 Model.clean_fields()Model.clean()Model.validate_unique() (如果 validate_uniqueTrue) 和 Model.validate_constraints() (如果 validate_constraintsTrue),並引發一個 ValidationError,其中具有一個 message_dict 屬性,其中包含來自所有四個階段的錯誤。

可選的 exclude 引數可用於提供一組 set 欄位名稱,這些欄位名稱可以排除在驗證和清理之外。ModelForm 使用此引數來排除表單上不存在的欄位,以免驗證,因為任何引發的錯誤都無法由使用者更正。

請注意,當您呼叫模型的 save() 方法時,不會自動呼叫 full_clean()。當您想要為自己手動建立的模型執行一步模型驗證時,您需要手動呼叫它。例如:

from django.core.exceptions import ValidationError

try:
    article.full_clean()
except ValidationError as e:
    # Do something based on the errors contained in e.message_dict.
    # Display them to a user, or handle them programmatically.
    pass

full_clean() 執行的第一個步驟是清理每個個別欄位。

Model.clean_fields(exclude=None)[原始碼]

此方法會驗證模型上的所有欄位。可選的 exclude 引數可讓您提供一組 set 欄位名稱,以排除在驗證之外。如果任何欄位未通過驗證,它將引發 ValidationError

full_clean() 執行的第二個步驟是呼叫 Model.clean()。應該覆寫此方法,以對模型執行自訂驗證。

Model.clean()[原始碼]

此方法應使用於提供自訂模型驗證,並在需要時修改模型上的屬性。例如,您可以使用它來自動為欄位提供值,或執行需要存取多個欄位的驗證。

import datetime
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _


class Article(models.Model):
    ...

    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == "draft" and self.pub_date is not None:
            raise ValidationError(_("Draft entries may not have a publication date."))
        # Set the pub_date for published items if it hasn't been set already.
        if self.status == "published" and self.pub_date is None:
            self.pub_date = datetime.date.today()

但是,請注意,與 Model.full_clean() 類似,當您呼叫模型的 save() 方法時,不會呼叫模型的 clean() 方法。

在上面的範例中,由 Model.clean() 引發的 ValidationError 例外是用字串實例化的,因此它將儲存在一個特殊的錯誤字典索引鍵 NON_FIELD_ERRORS 中。此索引鍵用於繫結到整個模型而不是特定欄位的錯誤。

from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

try:
    article.full_clean()
except ValidationError as e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

若要將例外指派給特定欄位,請使用字典實例化 ValidationError,其中索引鍵為欄位名稱。我們可以更新先前的範例,以將錯誤指派給 pub_date 欄位。

class Article(models.Model):
    ...

    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == "draft" and self.pub_date is not None:
            raise ValidationError(
                {"pub_date": _("Draft entries may not have a publication date.")}
            )
        ...

如果您在 Model.clean() 期間偵測到多個欄位中的錯誤,您也可以傳遞一個將欄位名稱對應到錯誤的字典。

raise ValidationError(
    {
        "title": ValidationError(_("Missing title."), code="required"),
        "pub_date": ValidationError(_("Invalid date."), code="invalid"),
    }
)

然後,full_clean() 將檢查模型上的唯一約束。

如果欄位未出現在 ModelForm 中,如何引發特定於欄位的驗證錯誤

您無法在 Model.clean() 中針對未出現在模型表單中的欄位引發驗證錯誤 (表單可能會使用 Meta.fieldsMeta.exclude 限制其欄位)。這樣做將會引發 ValueError,因為驗證錯誤將無法與排除的欄位建立關聯。

若要解決此困境,請改為覆寫 Model.clean_fields(),因為它會收到從驗證中排除的欄位清單。例如:

class Article(models.Model):
    ...

    def clean_fields(self, exclude=None):
        super().clean_fields(exclude=exclude)
        if self.status == "draft" and self.pub_date is not None:
            if exclude and "status" in exclude:
                raise ValidationError(
                    _("Draft entries may not have a publication date.")
                )
            else:
                raise ValidationError(
                    {
                        "status": _(
                            "Set status to draft if there is not a publication date."
                        ),
                    }
                )
Model.validate_unique(exclude=None)[原始碼]

此方法與 clean_fields() 類似,但會驗證透過 Field.uniqueField.unique_for_dateField.unique_for_monthField.unique_for_year 或模型上的 Meta.unique_together 定義的唯一性約束,而不是個別欄位值。可選的 exclude 引數可讓您提供一組 set 欄位名稱,以排除在驗證之外。如果任何欄位未通過驗證,它將引發 ValidationError

UniqueConstraint 中定義的 Meta.constraints 會由 Model.validate_constraints() 進行驗證。

請注意,如果您向 validate_unique() 提供 exclude 參數,則任何涉及您提供的欄位的 unique_together 約束條件將不會被檢查。

最後,full_clean() 將檢查模型上的任何其他約束條件。

Model.validate_constraints(exclude=None)[原始碼]

此方法會驗證在 Meta.constraints 中定義的所有約束條件。可選的 exclude 參數允許您提供一個欄位名稱的 set,將其排除在驗證之外。如果任何約束條件驗證失敗,它將引發 ValidationError

儲存物件

要將物件儲存回資料庫,請呼叫 save()

Model.save(*, force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)[原始碼]
Model.asave(*, force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)

非同步版本asave()

有關使用 force_insertforce_update 參數的詳細資訊,請參閱 強制執行 INSERT 或 UPDATE。有關 update_fields 參數的詳細資訊,請參閱 指定要儲存的欄位 章節。

如果您想要自訂儲存行為,您可以覆寫此 save() 方法。有關更多詳細資訊,請參閱 覆寫預定義的模型方法

模型儲存過程也有一些微妙之處;請參閱下面的章節。

自 5.1 版本起已棄用: 對位置參數的支援已被棄用。

自動遞增主鍵

如果模型具有 AutoField — 自動遞增主鍵 — 則該自動遞增的值將在您第一次呼叫 save() 時計算並作為物件的屬性儲存。

>>> b2 = Blog(name="Cheddar Talk", tagline="Thoughts on cheese.")
>>> b2.id  # Returns None, because b2 doesn't have an ID yet.
>>> b2.save()
>>> b2.id  # Returns the ID of your new object.

在您呼叫 save() 之前,無法知道 ID 的值,因為該值是由您的資料庫計算,而不是由 Django 計算。

為了方便起見,每個模型預設都有一個名為 idAutoField,除非您在模型中的欄位上明確指定 primary_key=True。有關更多詳細資訊,請參閱 AutoField 的文件。

pk 屬性

Model.pk

無論您是自己定義主鍵欄位,還是讓 Django 為您提供主鍵欄位,每個模型都會有一個名為 pk 的屬性。它的行為就像模型上的普通屬性,但實際上是模型主鍵欄位的別名。您可以像對待任何其他屬性一樣讀取和設定此值,它將更新模型中的正確欄位。

明確指定自動主鍵值

如果模型具有 AutoField,但您希望在儲存時明確定義新物件的 ID,則在儲存之前明確定義它,而不是依賴 ID 的自動分配。

>>> b3 = Blog(id=3, name="Cheddar Talk", tagline="Thoughts on cheese.")
>>> b3.id  # Returns 3.
>>> b3.save()
>>> b3.id  # Returns 3.

如果您手動分配自動主鍵值,請確保不要使用已存在的主鍵值!如果您建立具有資料庫中已存在的主鍵值的新物件,Django 會假設您正在變更現有記錄,而不是建立新記錄。

考慮到上面的 'Cheddar Talk' 網誌範例,此範例將覆寫資料庫中的先前記錄。

b4 = Blog(id=3, name="Not Cheddar", tagline="Anything but cheese.")
b4.save()  # Overrides the previous blog with ID=3!

有關發生這種情況的原因,請參閱下面的 Django 如何知道要 UPDATE 與 INSERT

明確指定自動主鍵值主要用於大量儲存物件,當您確信不會發生主鍵衝突時。

如果您使用 PostgreSQL,則可能需要更新與主鍵關聯的序列;請參閱 手動指定自動遞增主鍵的值

當您儲存時會發生什麼?

當您儲存物件時,Django 會執行以下步驟:

  1. 發出 pre-save 訊號。發送 pre_save 訊號,允許任何監聽該訊號的函式執行某些操作。

  2. 預處理資料。呼叫每個欄位的 pre_save() 方法,以執行任何所需的自動資料修改。例如,日期/時間欄位會覆寫 pre_save() 以實作 auto_now_addauto_now

  3. 準備資料以供資料庫使用。要求每個欄位的 get_db_prep_save() 方法以資料庫可寫入的資料類型提供其目前值。

    大多數欄位不需要資料準備。簡單的資料類型(例如整數和字串)作為 Python 物件「已準備好寫入」。但是,更複雜的資料類型通常需要進行一些修改。

    例如,DateField 欄位使用 Python 的 datetime 物件來儲存資料。資料庫不會儲存 datetime 物件,因此欄位值必須轉換成符合 ISO 標準的日期字串,才能插入資料庫。

  4. 將資料插入資料庫。 預處理過的、準備好的資料會被組合成 SQL 陳述式,以便插入資料庫。

  5. 發出儲存後訊號。 會發送 post_save 訊號,允許任何監聽該訊號的函式執行某些操作。

Django 如何判斷要執行 UPDATE 還是 INSERT

您可能已經注意到 Django 資料庫物件使用相同的 save() 方法來建立和變更物件。Django 將使用 INSERTUPDATE SQL 陳述式的需求抽象化了。具體來說,當您呼叫 save() 並且物件的主鍵屬性定義 defaultdb_default 時,Django 會遵循此演算法

  • 如果物件的主鍵屬性被設定為評估結果為 True 的值(也就是說,不是 None 或空字串的值),Django 會執行 UPDATE

  • 如果物件的主鍵屬性設定,或者如果 UPDATE 沒有更新任何內容(例如,如果主鍵被設定為資料庫中不存在的值),Django 會執行 INSERT

如果物件的主鍵屬性定義了 defaultdb_default,那麼如果它是現有的模型實例並且主鍵被設定為資料庫中存在的值,Django 會執行 UPDATE。否則,Django 會執行 INSERT

這裡需要注意的一點是,當儲存新物件時,如果無法保證主鍵值未被使用,則應小心不要明確指定主鍵值。有關此細微差別的更多資訊,請參閱上方「明確指定自動主鍵值」和下方「強制執行 INSERT 或 UPDATE」。

在 Django 1.5 和更早版本中,當設定主鍵屬性時,Django 會執行 SELECT。如果 SELECT 找到一列,則 Django 會執行 UPDATE,否則會執行 INSERT。舊的演算法在 UPDATE 情況下會多執行一個查詢。在極少數情況下,即使資料庫包含該物件主鍵值的列,資料庫也不會回報已更新列。例如,PostgreSQL 的 ON UPDATE 觸發器會傳回 NULL。在這種情況下,可以將 select_on_save 選項設定為 True,以恢復舊的演算法。

Django 5.0 版已變更

新增了 Field.db_default 參數。

強制執行 INSERT 或 UPDATE

在某些極少數情況下,有必要能夠強制 save() 方法執行 SQL INSERT,而不是退回到執行 UPDATE。反之亦然:如果可能,執行更新,但不插入新列。在這些情況下,您可以將 force_insert=Trueforce_update=True 參數傳遞給 save() 方法。同時傳遞兩個參數是錯誤的:您不能同時執行插入更新!

當使用多表繼承時,也可以為 force_insert 提供父類別的元組,以便強制為每個基類執行 INSERT 陳述式。例如

Restaurant(pk=1, name="Bob's Cafe").save(force_insert=(Place,))

Restaurant(pk=1, name="Bob's Cafe", rating=4).save(force_insert=(Place, Rating))

您可以傳遞 force_insert=(models.Model,) 以強制為所有父類別執行 INSERT 陳述式。預設情況下,force_insert=True 僅強制為目前模型插入新列。

您很少需要使用這些參數。Django 幾乎總是會做正確的事情,而嘗試覆寫它會導致難以追蹤的錯誤。此功能僅供進階使用。

使用 update_fields 會強制執行更新,類似於 force_update

Django 5.0 版已變更

新增了對將父類別的元組傳遞給 force_insert 的支援。

根據現有欄位更新屬性

有時,您需要在欄位上執行簡單的算術運算,例如遞增或遞減目前值。實現此目的的一種方法是在 Python 中執行算術運算,如下所示:

>>> product = Product.objects.get(name="Venezuelan Beaver Cheese")
>>> product.number_sold += 1
>>> product.save()

如果從資料庫檢索的舊 number_sold 值為 10,則值 11 將寫回資料庫。

透過將更新表示為相對於原始欄位值,而不是明確指定新值,可以使該過程更加穩健,避免競爭條件,並略微加快速度。Django 提供了 F 運算式 來執行這種相對更新。使用 F 運算式,前面的範例表示為

>>> from django.db.models import F
>>> product = Product.objects.get(name="Venezuelan Beaver Cheese")
>>> product.number_sold = F("number_sold") + 1
>>> product.save()

有關更多詳細資訊,請參閱有關 F 運算式 及其在更新查詢中的使用的文件。

指定要儲存的欄位

如果在關鍵字引數 update_fields 中傳遞欄位名稱清單給 save(),則只會更新該清單中命名的欄位。如果您只想更新物件的一個或幾個欄位,這可能是理想的選擇。防止所有模型欄位在資料庫中被更新會帶來輕微的效能提升。例如

product.name = "Name changed again"
product.save(update_fields=["name"])

update_fields 引數可以是任何包含字串的可迭代物件。空的 update_fields 可迭代物件將會跳過儲存。值 None 將會對所有欄位執行更新。

指定 update_fields 將強制執行更新。

當儲存透過延遲模型載入擷取的模型(only()defer())時,只會更新從資料庫載入的欄位。實際上,在這種情況下會有自動的 update_fields。如果您指定或變更任何延遲欄位值,該欄位將會新增到已更新的欄位中。

Field.pre_save()update_fields

如果傳遞了 update_fields,則只會呼叫 update_fieldspre_save() 方法。例如,這表示除非包含在 update_fields 中,否則 auto_now=True 的日期/時間欄位將不會更新。

刪除物件

Model.delete(using=DEFAULT_DB_ALIAS, keep_parents=False)[原始碼]
Model.adelete(using=DEFAULT_DB_ALIAS, keep_parents=False)

非同步版本adelete()

對物件發出 SQL DELETE 指令。這只會刪除資料庫中的物件;Python 實例仍然存在,並且其欄位中仍會保留資料,除了主鍵會被設定為 None。此方法會回傳刪除的物件數量以及一個字典,其中包含每個物件類型刪除的數量。

如需更多詳細資訊,包括如何批量刪除物件,請參閱 刪除物件

如果您想要自訂刪除行為,可以覆寫 delete() 方法。請參閱 覆寫預定義的模型方法 以了解更多詳細資訊。

有時使用 多表格繼承 時,您可能只想刪除子模型的資料。指定 keep_parents=True 將會保留父模型的資料。

封存物件

當您 pickle 模型時,其目前的狀態會被封存。當您解封存它時,它會包含封存時的模型實例,而不是資料庫中當前的資料。

您無法在不同版本之間共享封存

模型的封存僅在用於產生它們的 Django 版本中有效。如果您使用 Django 版本 N 產生一個封存,則無法保證該封存在 Django 版本 N+1 中可讀取。封存不應作為長期歸檔策略的一部分。

由於封存相容性錯誤可能難以診斷,例如靜默損壞的物件,當您嘗試在與封存時不同的 Django 版本中解封存模型時,會引發 RuntimeWarning

其他模型實例方法

一些物件方法有特殊用途。

__str__()

Model.__str__()[原始碼]

當您在物件上呼叫 str() 時,會呼叫 __str__() 方法。Django 在許多地方使用 str(obj)。最值得注意的是,在 Django 管理站點中顯示物件,以及在顯示物件時作為插入到模板中的值。因此,您應該始終從 __str__() 方法回傳模型的一個良好、人類可讀的表示。

例如

from django.db import models


class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

__eq__()

Model.__eq__()[原始碼]

相等方法的定義是,具有相同主鍵值和相同具體類別的實例被認為是相等的,除了主鍵值為 None 的實例與除了它們本身之外的任何事物都不相等。對於代理模型,具體類別定義為模型的第一個非代理父類別;對於所有其他模型,它只是模型的類別。

例如

from django.db import models


class MyModel(models.Model):
    id = models.AutoField(primary_key=True)


class MyProxyModel(MyModel):
    class Meta:
        proxy = True


class MultitableInherited(MyModel):
    pass


# Primary keys compared
MyModel(id=1) == MyModel(id=1)
MyModel(id=1) != MyModel(id=2)
# Primary keys are None
MyModel(id=None) != MyModel(id=None)
# Same instance
instance = MyModel(id=None)
instance == instance
# Proxy model
MyModel(id=1) == MyProxyModel(id=1)
# Multi-table inheritance
MyModel(id=1) != MultitableInherited(id=1)

__hash__()

Model.__hash__()[原始碼]

__hash__() 方法基於實例的主鍵值。它實際上是 hash(obj.pk)。如果實例沒有主鍵值,則會引發 TypeError(否則,__hash__() 方法會在實例儲存前後回傳不同的值,但更改實例的 __hash__() 值在 Python 中是被禁止的)。

get_absolute_url()

Model.get_absolute_url()

定義一個 get_absolute_url() 方法來告訴 Django 如何計算物件的規範 URL。對於呼叫者,此方法應顯示為回傳一個可用於透過 HTTP 參照物件的字串。

例如

def get_absolute_url(self):
    return "/people/%i/" % self.id

雖然此程式碼正確且簡單,但它可能不是編寫此類方法最可移植的方式。reverse() 函式通常是最佳方法。

例如

def get_absolute_url(self):
    from django.urls import reverse

    return reverse("people-detail", kwargs={"pk": self.pk})

Django 使用 get_absolute_url() 的一個地方是在管理應用程式中。如果物件定義了此方法,則物件編輯頁面將會有一個「在網站上檢視」連結,該連結會將您直接跳轉到物件的公開檢視,如 get_absolute_url() 所提供。

同樣地,Django 的其他一些部分,例如 聯合訂閱框架,在定義 get_absolute_url() 時也會使用它。如果您的模型實例都各自擁有唯一 URL 是有意義的,則應該定義 get_absolute_url()

警告

您應該避免從未驗證的使用者輸入建構 URL,以減少連結或重新導向中毒的可能性

def get_absolute_url(self):
    return "/%s/" % self.name

如果 self.name'/example.com',這會回傳 '//example.com/',而這反過來又是一個有效的綱要相對 URL,但不是預期的 '/%2Fexample.com/'

在模板中使用 get_absolute_url(),而不是硬式編碼您的物件 URL 是一種良好的做法。例如,此模板程式碼是不好的

<!-- BAD template code. Avoid! -->
<a href="/people/{{ object.id }}/">{{ object.name }}</a>

此模板程式碼更好

<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>

這裡的邏輯是,如果您變更物件的 URL 結構,即使是像更正拼寫錯誤這樣的小變更,您也不想追蹤每個可能建立 URL 的位置。在 get_absolute_url() 中指定一次,並讓您的所有其他程式碼呼叫該位置。

注意

您從 get_absolute_url() 回傳的字串必須僅包含 ASCII 字元(URI 規範所要求,RFC 3986 第 2 節)並且在必要時進行 URL 編碼。

呼叫 get_absolute_url() 的程式碼和模板應該能夠直接使用結果,而無需任何進一步處理。如果您使用包含 ASCII 範圍之外的字元的字串,您可能會希望使用 django.utils.encoding.iri_to_uri() 函式來協助處理此問題。

額外的實例方法

除了 save()delete() 之外,模型物件可能還具有以下一些方法

Model.get_FOO_display()

對於每個設定了 choices 的欄位,物件將會擁有一個 get_FOO_display() 方法,其中 FOO 是欄位的名稱。此方法會回傳欄位的「人類可讀」值。

例如

from django.db import models


class Person(models.Model):
    SHIRT_SIZES = {
        "S": "Small",
        "M": "Medium",
        "L": "Large",
    }
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
Model.get_next_by_FOO(**kwargs)
Model.get_previous_by_FOO(**kwargs)

對於每個沒有設定 null=TrueDateFieldDateTimeField,物件將會有 get_next_by_FOO()get_previous_by_FOO() 方法,其中 FOO 是欄位的名稱。這會返回相對於日期欄位的下一個和上一個物件,並在適當的時候引發 DoesNotExist 例外。

這兩個方法都會使用模型的預設管理器執行查詢。如果您需要模擬自訂管理器使用的篩選,或是想要執行一次性的自訂篩選,這兩個方法也接受選用的關鍵字參數,這些參數應該採用 欄位查找 中描述的格式。

請注意,在日期值相同的情況下,這些方法會使用主鍵作為平手打破器。這保證不會跳過或重複任何記錄。這也意味著您不能在未儲存的物件上使用這些方法。

覆寫額外的實例方法

在大多數情況下,覆寫或繼承 get_FOO_display()get_next_by_FOO()get_previous_by_FOO() 應該會如預期般運作。然而,由於它們是由元類別添加的,因此要考慮所有可能的繼承結構是不切實際的。在更複雜的情況下,您應該覆寫 Field.contribute_to_class() 來設定您需要的方法。

其他屬性

_state

Model._state

_state 屬性指的是一個 ModelState 物件,該物件追蹤模型實例的生命週期。

ModelState 物件有兩個屬性:adding,一個旗標,如果模型尚未儲存到資料庫,則為 True;以及 db,一個字串,指的是實例從哪個資料庫別名載入或儲存。

新建立的實例具有 adding=Truedb=None,因為它們尚未儲存。從 QuerySet 取得的實例將具有 adding=Falsedb 設定為相關資料庫的別名。

返回頂部