Django比较模型实例是否相等

Django comparing model instances for equality

我理解,在单例情况下,您可以执行以下操作:

1
spam == eggs

如果spameggs是具有相同属性值的同一类的实例,则返回True。在Django模型中,这是自然的,因为一个模型的两个独立实例将永远不会相同,除非它们具有相同的.pk值。

问题在于,如果对一个实例的引用具有一些属性,这些属性已经在某个地方被中间件更新,并且还没有保存,并且您试图将其保存到另一个变量中,该变量包含对同一个模型的实例的引用,那么它将返回False,当然,因为它们对于某些实例具有不同的值。属性。显然,我不需要像singleton这样的东西,但是我想知道是否有一些官方的djangonic(ha,一个新词)方法来检查这个,或者我应该简单地检查.pk值是否与以下值相同:

1
spam.pk == eggs.pk

如果这是浪费时间的话,我很抱歉,但似乎有一种方法可以做到这一点,如果我找不到的话,我会后悔的。

更新(2015年2月27日)

你应该忽略这个问题的第一部分,因为你不应该将单件货物与==进行比较,而应该与is进行比较。单身与这个问题毫无关系。


来自Django文档:

要比较两个模型实例,只需使用标准的python比较运算符,双等号:==。在幕后,比较两个模型的主键值。


spam.pk == eggs.pk是一个很好的方法。

你可以在你的模型中添加__eq__,但我会避免这一点,因为==可以在不同的上下文中表示不同的东西,例如我可能希望==表示内容相同,ID可能不同,所以最好的方法是

1
spam.pk == eggs.pk

编辑:django 1.0.2模型类中的btw将__eq__定义为

1
2
def __eq__(self, other):
    return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val()

这似乎与spam.pk==eggs.pk相同,因为pk是使用_get_pk_val的属性。所以我不明白为什么spam == eggs不起作用?


从Django 2.1.2开始,模型实例等式的源代码如下:

1
2
3
4
5
6
7
8
9
def __eq__(self, other):
    if not isinstance(other, Model):
        return False
    if self._meta.concrete_model != other._meta.concrete_model:
        return False
    my_pk = self.pk
    if my_pk is None:
        return self is other
    return my_pk == other.pk

也就是说,如果两个模型实例来自同一个数据库表并且具有相同的主键,那么这两个模型实例是相等的。如果其中一个主键是None,那么只有当它们是同一对象时,它们才相等。

(所以回到OP的问题,简单地比较实例就可以了。)


您可以定义类"__eq__方法来改变这种行为:

http://docs.python.org/reference/datamodel.html网站


只是为了记录,比较一下:

1
    spam == eggs

如果它们中的任何一个可能是由model.objects.raw()查询或由应用于"normal"查询集的.defer()创建的延迟模型实例,则是危险的。

我把更多的细节放在这里:django queryset.defer()问题-bug还是特性?


正如Orokusaki所说,"如果两个实例都没有主键,它将始终返回true"。如果您希望这样做,可以像这样扩展您的模型:

1
2
3
4
5
6
def __eq__(self, other):
    eq = isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val()

    if eq and self._get_pk_val() is None:
        return id(self) == id(other)
    return eq


如果两个模型实例具有不同的属性,将它们比较为相等,这会很奇怪。大多数时候这是不受欢迎的。

你想要的是一个特例。比较spam.pk == eggs.pk是个好主意。如果还没有pk,因为它们还没有被保存,那么如果某些属性不同,那么很难定义哪些实例是"真正"相同的。

在创建实例时,如何向其添加自定义属性,例如:江户十一〔11〕、江户十一〔12〕。

在代码中的某个时候,当spamcopy1.seasoning=ketchupspamcopy2.seasoning=blackpepper的属性比较它们的myid属性时,可以看到它们是否真的是"相同"的垃圾邮件。