【发布时间】:2011-01-03 11:39:00
【问题描述】:
我通常会检查if obj.pk 以了解对象是否已保存。但是,如果您在某些字段上设置了primary_key = True,这将不起作用。例如,我在UserProfile 上设置了user = models.OneToOneField(User, primary_key=True)。
确定 Django 模型是否保存到 db 的规范方法是什么?
【问题讨论】:
标签: django django-models
我通常会检查if obj.pk 以了解对象是否已保存。但是,如果您在某些字段上设置了primary_key = True,这将不起作用。例如,我在UserProfile 上设置了user = models.OneToOneField(User, primary_key=True)。
确定 Django 模型是否保存到 db 的规范方法是什么?
【问题讨论】:
标签: django django-models
重要提示(截至 2019 年 5 月 6 日):如果您的模型使用 UUID 字段(或其他内部 ID 生成方法,请使用 cmets 中提到的 self._state.adding。
其实obj.pk是最规范的方式。 Django 本身通常不“知道”对象是否被保存。根据the django model instance reference,如果已经设置了主键,它会通过在任何插入之前选择数据库中的id来检查save()调用。
即使您设置了user = models.OneToOneField(..., primary_key=True),.pk 属性仍将指向正确的主键(很可能是user_id),您可以使用它并将其设置为相同的属性。
如果你想知道一个对象被保存后,你可以抓住post_save signal。此信号在模型保存时触发,如果您愿意,可以将自己的应用程序特定属性添加到模型中,例如 obj.was_saved = True。我认为 django 避免这样做是为了保持他们的实例干净,但是没有真正的理由你不能自己做这件事。这是一个最小的例子:
from django.db.models.signals import post_save
from myapp.models import MyModel
def save_handler(sender, instance, **kwargs):
instance.was_saved = True
post_save.connect(save_handler, sender=MyModel)
您也可以通过简单地连接信号而不指定sender= 参数来让此功能适用于您应用中的所有 模型。但请注意,如果您在您正在导入的其他人的模型实例上覆盖一个属性,您可能会创建未定义的行为。
【讨论】:
created,所以不需要插入你自己的。
self._state.adding。 stackoverflow.com/a/19379636/23972
假设obj 是MyModel 的一个实例。然后我们可以使用以下代码块来检查数据库中是否已经存在具有该主键的实例:
if obj.pk is None:
# Definitely doesn't exist, since there's no `pk`.
exists = False
else:
# The `pk` is set, but it doesn't guarantee exists in db.
try:
obj_from_db = MyModel.objects.get(pk=obj.pk)
exists = True
except MyModel.DoesNotExist:
exists = False
这比检查obj.pk is None是否更好,因为你可以这样做
obj = MyModel()
obj.pk = 123
然后
obj.pk is None # False
当您不使用自动增量 id 字段作为主键而是使用自然键时,这甚至很有可能。
或者,正如 Matthew 在 cmets 中指出的那样,您可以这样做
obj.delete()
之后你还有
obj.pk is None # False
【讨论】:
现在您可以检查:
self._state.adding
此值由QuerySet.iterator() 为尚未添加到数据库中的对象设置。您还不能在 __init__() 方法中使用此值,因为它是在对象构建后设置的。
【讨论】:
Model.from_db()
self._state.adding == True 表示“尚未添加到数据库中”。见:docs.djangoproject.com/en/1.8/ref/models/instances
Model.from_db() 的相关性?
@Crast 的回答很好,但我认为不完整。我在单元测试中用于确定对象是否在数据库中的代码如下。下面,我将解释为什么我认为它优于检查obj.pk is None。
from django.test import TestCase
class TestCase(TestCase):
def assertInDB(self, obj, msg=None):
"""Test for obj's presence in the database."""
fullmsg = "Object %r unexpectedly not found in the database" % obj
fullmsg += ": " + msg if msg else ""
try:
type(obj).objects.get(pk=obj.pk)
except obj.DoesNotExist:
self.fail(fullmsg)
def assertNotInDB(self, obj, msg=None):
"""Test for obj's absence from the database."""
fullmsg = "Object %r unexpectedly found in the database" % obj
fullmsg += ": " + msg if msg else ""
try:
type(obj).objects.get(pk=obj.pk)
except obj.DoesNotExist:
return
else:
self.fail(fullmsg)
注意:如果您在模型名称上使用除 objects 以外的自定义管理器,请谨慎使用上述代码。 (我确信有办法让 Django 告诉你默认管理器是什么。)此外,我知道 /assert(Not)?InDB/ 不是 PEP 8 方法名称,但我使用了 unittest 包的其余部分的样式用过。
我认为assertInDB(obj)优于assertIsNotNone(obj.pk)的原因是因为以下情况。假设您有以下模型。
from django.db import models
class Node(models.Model):
next = models.OneToOneField('self', null=True, related_name='prev')
Node 模拟一个双向链表:您可以使用外键将任意数据附加到每个节点,尾部是 Node obj 使得 obj.next is None。默认情况下,Django 将 SQL 约束 ON DELETE CASCADE 添加到 Node 的主键中。现在,假设您有一个长度为 n 的list nodes 使得nodes[i].next == nodes[i + 1] for i in [0, n - 1)。假设您拨打nodes[0].delete()。在我对 Python 3.3 上的 Django 1.5.1 的测试中,我发现 nodes[i].pk is not None 用于 [1, n) 中的 i 并且只有 nodes[0].pk is None。但是,我上面的 /assert(Not)?InDB/ 方法正确检测到 [1, n) 中 i 的 nodes[i] 确实已被删除。
【讨论】:
nodes 列表中的项目是实例化的模型对象,因此不再受数据库中发生的任何事情的影响。检查pk is not None 至少可以告诉您该实例是否在某个时候 已保存到数据库中(...假设没有人手动为实例上的pk 分配值)跨度>
assertInDb 的更好选择是:obj.refresh_from_db(),assertNotInDb:with self.assertRaises(obj.DoesNotExist): obj.refresh_from_db()。