找出Django模型是否保存到db的规范方法是什么?

时间:2010-01-10 14:43:56

标签: django django-models

如果保存了对象,我通常会检查if obj.pk knwo。但是,如果您在某些字段上设置primary_key = True,则无法使用此功能。例如,我在user = models.OneToOneField(User, primary_key=True)上设置了UserProfile

找出Django模型是否保存到db的规范方法是什么?

4 个答案:

答案 0 :(得分:35)

重要提示(截至19年5月6日):如果您的模型使用UUID字段(或其他内部ID生成方法,请使用评论中提到的self._state.adding

实际上,obj.pk是最规范的方式。 Django本身通常不会“知道”对象是否被保存。根据{{​​3}},如果已经存在主键,它会在任何插入之前通过在数据库中选择id来检查save()个调用。

即使您设置user = models.OneToOneField(..., primary_key=True).pk属性仍会指向正确的主键(最有可能是user_id),您可以使用它并将其设置为相同属性。

如果您想知道保存对象后,可以抓住the django model instance reference。此信号在模型保存时触发,如果需要,您可以将自己的特定于应用程序的属性添加到模型中,例如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=参数,将此功能用于应用中的所有模型。但要注意,如果您覆盖要导入的其他人的模型实例上的属性,则可以创建未定义的行为。

答案 1 :(得分:30)

现在您可以查看:

self._state.adding

对于尚未在数据库中添加的对象,此值由QuerySet.iterator()设置。您无法在__init__()方法中使用此值,因为它是在构造对象后设置的。

答案 2 :(得分:9)

让我们说objMyModel的一个实例。然后我们可以使用以下代码块来检查数据库中是否已存在具有该主键的实例:

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字段作为主键但是使用自然增量字段时,这甚至非常有可能。

或者,正如马修在评论中指出的那样,你可以做到

obj.delete()

之后你还有

obj.pk is None  # False

答案 3 :(得分:3)

@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方法名称,但我使用了{{1}的其余部分的样式。 1}}使用的包。

理由

我认为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 obj ,以便Node 。默认情况下,Django将SQL约束obj.next is None添加到ON DELETE CASCADE的主键。现在,假设您有一个Node 个节点,其长度为 n ,因此{0}中的 i list em> n - 1)。假设您致电nodes[i].next == nodes[i + 1]。在我对Python 3.3上的Django 1.5.1的测试中,我在[1, n )中找到了{em> i 的nodes[0].delete()并且仅nodes[i].pk is not None。但是,我上面的nodes[0].pk is None方法正确检测到[1, n i /assert(Not)?InDB/确实已被删除。