模型实例参考¶
本文档详细介绍了 Model API。它建立在 模型 (model) 和 数据库查询 (database query) 指南所呈现内容的基础上,因此建议您在阅读本文之前先阅读并理解这些文档。
在本参考指南中,我们将使用 数据库查询指南 中介绍的 示例博客模型。
创建对象¶
要创建模型的新实例,像实例化任何其他 Python 类一样实例化它即可
关键字参数是您在模型中定义的字段名称。请注意,实例化模型绝不会触及您的数据库;要执行此操作,您需要调用 save()。
注意
您可能会想通过覆盖 __init__ 方法来自定义模型。然而,如果您这样做,请注意不要更改调用签名,因为任何更改都可能导致模型实例无法保存。此外,在 __init__ 中引用模型字段在某些情况下可能会导致无限递归错误。与其覆盖 __init__,不如尝试使用以下方法之一
在模型类上添加类方法 (classmethod)
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")
在自定义管理器 (custom manager) 上添加方法(通常更推荐)
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")
设置属性不会转换类型
设置模型属性不会将值转换为该字段的 Python 类型。该转换发生在模型被 验证 (validated) 时。尽管当 save() 执行查询时,被赋予不同类型值的字段(例如,赋值给 UUIDField 的字符串)可能会被转换,但内存中的实例将保留原始值,直到它被 重新加载 (reloaded)。
自定义模型加载¶
from_db() 方法可用于在从数据库加载时自定义模型实例的创建。
db 参数包含加载模型的数据库别名,field_names 包含所有已加载字段的名称,values 包含 field_names 中每个字段的已加载值。field_names 的顺序与 values 的顺序相同。如果模型的所有字段都存在,则 values 保证按照 __init__() 期望的顺序排列。也就是说,实例可以通过 cls(*values) 创建。如果存在任何延迟字段,它们将不会出现在 field_names 中。在这种情况下,请为每个缺失的字段赋值 django.db.models.DEFERRED。
除了创建新模型外,from_db() 方法还必须在新实例的 _state 属性中设置 adding 和 db 标志。
下面是一个展示如何记录从数据库加载的字段初始值的示例
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.arefresh_from_db(using=None, fields=None, from_queryset=None)¶
异步版本: arefresh_from_db()
如果您需要从数据库重新加载模型的值,可以使用 refresh_from_db() 方法。当不带参数调用此方法时,将执行以下操作
模型的所有非延迟字段都将更新为数据库中当前存在的值。
任何缓存的关系都将从重新加载的实例中清除。
只有模型的字段会从数据库中重新加载。其他依赖数据库的值(如注解)不会重新加载。任何 @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 创建的查询集不同的查询集。它使您可以更好地控制模型的重新加载方式。例如,当您的模型使用软删除 (soft deletion) 时,您可以使 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())
一个辅助方法,返回一个集合,包含该模型当前所有被延迟的字段名称。
验证对象¶
验证模型涉及四个步骤
验证模型字段 -
Model.clean_fields()验证整个模型 -
Model.clean()验证字段唯一性 -
Model.validate_unique()验证约束 -
Model.validate_constraints()
当您调用模型的 full_clean() 方法时,这四个步骤都会执行。
当您使用 ModelForm 时,调用 is_valid() 将对表单中包含的所有字段执行这些验证步骤。有关详细信息,请参阅 ModelForm 文档。您只有在计划自己处理验证错误,或者在从 ModelForm 中排除了需要验证的字段时,才需要调用模型的 full_clean() 方法。
此方法按顺序调用 Model.clean_fields()、Model.clean()、Model.validate_unique()(如果 validate_unique 为 True),以及 Model.validate_constraints()(如果 validate_constraints 为 True),并抛出一个包含 message_dict 属性的 ValidationError,其中包含来自所有四个阶段的错误。
可选的 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() 执行的第一步是清理每个单独的字段。
此方法将验证模型上的所有字段。可选的 exclude 参数允许您提供一个要从验证中排除的字段名称 set。如果任何字段验证失败,它将抛出一个 ValidationError。
full_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.fields 或 Meta.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."
),
}
)
此方法类似于 clean_fields(),但验证的是通过 Field.unique、Field.unique_for_date、Field.unique_for_month、Field.unique_for_year 或模型上的 Meta.unique_together 定义的唯一性约束,而不是单个字段值。可选的 exclude 参数允许您提供一个要从验证中排除的字段名称 set。如果任何字段验证失败,它将抛出一个 ValidationError。
在 Meta.constraints 中定义的 UniqueConstraint 由 Model.validate_constraints() 验证。
请注意,如果您为 validate_unique() 提供了 exclude 参数,则任何包含您提供的字段之一的 unique_together 约束都不会被检查。
最后,full_clean() 将检查模型上的任何其他约束。
此方法验证 Meta.constraints 中定义的所有约束。可选的 exclude 参数允许您提供一个要从验证中排除的字段名称 set。如果任何约束验证失败,它将抛出一个 ValidationError。
保存对象¶
要将对象保存回数据库,调用 save()
- Model.save(*, force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)[source]¶
- Model.asave(*, force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)¶
异步版本: asave()
有关使用 force_insert 和 force_update 参数的详细信息,请参阅 强制 INSERT 或 UPDATE。关于 update_fields 参数的详细信息可以在 指定要保存的字段 部分找到。
如果您想要自定义保存行为,可以覆盖此 save() 方法。有关更多详细信息,请参阅 覆盖预定义模型方法。
模型保存过程也有一些微妙之处;请参阅下面的部分。
自动递增主键¶
如果模型具有 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 计算的。
为方便起见,每个模型默认都有一个名为 id 的 AutoField,除非您在模型中的字段上显式指定了 primary_key=True。有关更多详细信息,请参阅 AutoField 的文档。
pk 属性¶
- Model.pk¶
无论您是自己定义主键字段还是让 Django 为您提供一个,每个模型都有一个名为 pk 的属性。它的行为就像模型上的普通属性,但实际上是构成模型主键的任何一个或多个字段的别名。您可以像对待任何其他属性一样读取和设置此值,它将更新模型中的正确字段。
通过 CompositePrimaryKey 添加了对由多个字段组成的主键的支持。
显式指定自动主键值¶
如果模型具有 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 会执行以下步骤
发出 pre-save 信号。 发送
pre_save信号,允许任何监听该信号的函数执行某些操作。预处理数据。 调用每个字段的
pre_save()方法以执行所需的任何自动数据修改。例如,日期/时间字段覆盖pre_save()以实现auto_now_add和auto_now。为数据库准备数据。 要求每个字段的
get_db_prep_save()方法以可以写入数据库的数据类型提供其当前值。大多数字段不需要数据准备。简单的数据类型(如整数和字符串)作为 Python 对象是“准备好写入”的。然而,更复杂的数据类型通常需要进行一些修改。
例如,
DateField字段使用 Pythondatetime对象来存储数据。数据库不存储datetime对象,因此必须将字段值转换为符合 ISO 标准的日期字符串才能插入数据库。将数据插入数据库。 将预处理和准备好的数据组合成 SQL 语句,以插入到数据库中。
发出 post-save 信号。 发送
post_save信号,允许任何监听该信号的函数执行某些操作。
Django 如何知道要执行 UPDATE 还是 INSERT¶
您可能已经注意到 Django 数据库对象使用相同的 save() 方法来创建和更改对象。Django 抽象了使用 INSERT 或 UPDATE SQL 语句的需求。具体来说,当您调用 save() 且对象的的主键属性未定义 default 或 db_default 时,Django 遵循此算法
如果对象的主键属性被设置为
None以外的任何值,Django 会执行UPDATE。如果对象的主键属性未设置,或者
UPDATE没有更新任何内容(例如,如果主键设置为数据库中不存在的值),Django 会执行INSERT。
如果对象的主键属性定义了 default 或 db_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 来回退到旧算法。
强制 INSERT 或 UPDATE¶
在某些极少数情况下,有必要强制 save() 方法执行 SQL INSERT 而不回退到执行 UPDATE。反之亦然:如果可能则更新,但不插入新行。在这些情况下,您可以将 force_insert=True 或 force_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 仅强制为当前模型插入新行。
当强制更新未影响任何行时,会抛出 NotUpdated 异常。在以前的版本中,会抛出通用的 django.db.DatabaseError。
您极少需要使用这些参数。Django 几乎总是会做正确的事情,尝试覆盖它将导致难以追踪的错误。此功能仅供高级使用。
使用 update_fields 将像 force_update 一样强制执行更新。
基于现有字段更新属性¶
有时您需要对字段执行简单的算术任务,例如递增或递减当前值。实现此目标的一种方法是在 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()
指定要保存的字段¶
如果将字段名称列表传递给关键字参数 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_fields 中字段的 pre_save() 方法。例如,这意味着 auto_now=True 的日期/时间字段将不会更新,除非它们包含在 update_fields 中。
删除对象¶
- Model.adelete(using=DEFAULT_DB_ALIAS, keep_parents=False)¶
异步版本: adelete()
为对象发出 SQL DELETE。这仅删除数据库中的对象;Python 实例仍然存在,并且其字段中仍然保留数据,除了主键被设置为 None。此方法返回已删除对象的数量以及一个包含每种对象类型删除数量的字典。
有关更多详细信息,包括如何批量删除对象,请参阅 删除对象。
如果您想自定义删除行为,可以覆盖 delete() 方法。有关更多详细信息,请参阅 覆盖预定义模型方法。
有时,通过 多表继承,您可能只想删除子模型的数据。指定 keep_parents=True 将保留父模型的数据。
序列化对象 (Pickling objects)¶
当您 pickle 一个模型时,其当前状态会被序列化。当您反序列化它时,它将包含模型被序列化瞬间的实例,而不是数据库中当前的数据。
其他模型实例方法¶
少数对象方法具有特殊用途。
__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__()¶
相等方法被定义为:具有相同主键值和相同具体类的实例被认为是相等的,除非主键值为 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__()¶
__hash__() 方法基于实例的主键值。它实际上是 hash(obj.pk)。如果实例没有主键值,则会抛出 TypeError(否则 __hash__() 方法在实例保存前后会返回不同的值,但 Python 禁止更改实例的 __hash__() 值)。
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 的其他一些部分,如 联合供稿框架 (syndication feed framework),在定义时也会使用 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=True 的 DateField 和 DateTimeField,对象都将拥有 get_next_by_FOO() 和 get_previous_by_FOO() 方法,其中 FOO 是字段的名称。这将返回关于该日期字段的下一个和上一个对象,并在合适时抛出 DoesNotExist 异常。
这两个方法都将使用模型的默认管理器执行其查询。如果您需要模拟由自定义管理器使用的过滤,或者想执行一次性的自定义过滤,这两个方法也都接受可选的关键字参数,这些参数应采用 字段查找 (Field lookups) 中描述的格式。
请注意,在日期值相同的情况下,这些方法将使用主键作为决胜依据。这保证了不会跳过或重复记录。这也意味着您不能在未保存的对象上使用这些方法。
覆盖额外实例方法
在大多数情况下,覆盖或继承 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=True和db=None,因为它们尚未保存。从QuerySet获取的实例将具有adding=False,并且db设置为关联数据库的别名。
_is_pk_set()¶
_is_pk_set() 方法返回模型实例的 pk 是否已设置。它抽象了模型的主键定义,确保无论具体的 pk 配置如何,行为都是一致的。