Django模型对象在升级到Django 2.2后变得不可散列

2024-06-13 10:24:05 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在测试应用程序从Django 2.1.7到2.2.12的更新。我在运行单元测试时出错,这归结为模型对象不可散列:

    Station.objects.all().delete()
py37\lib\site-packages\django\db\models\query.py:710: in delete
    collector.collect(del_query)
py37\lib\site-packages\django\db\models\deletion.py:192: in collect
    reverse_dependency=reverse_dependency)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <django.db.models.deletion.Collector object at 0x000001EC78243E80>
objs = <QuerySet [<Station(nom='DUNKERQUE')>, <Station(nom='STATION1')>, <Station(nom='STATION2')>]>, source = None, nullable = False
reverse_dependency = False

    def add(self, objs, source=None, nullable=False, reverse_dependency=False):
        """
        Add 'objs' to the collection of objects to be deleted.  If the call is
        the result of a cascade, 'source' should be the model that caused it,
        and 'nullable' should be set to True if the relation can be null.

        Return a list of all objects that were not already collected.
        """
        if not objs:
            return []
        new_objs = []
        model = objs[0].__class__
        instances = self.data.setdefault(model, set())
        for obj in objs:
>           if obj not in instances:
E           TypeError: unhashable type: 'Station'

Django中模型对象are hashable的实例,一旦保存到数据库并获得主键

我不明白错误来自何处,也不明白为什么在运行此基本代码时会出现这种情况:

In [7]: s = Station.objects.create(nom='SOME PLACE')

In [8]: hash(s)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-9333020f3184> in <module>
----> 1 hash(s)

TypeError: unhashable type: 'Station'

In [9]: s.pk
Out[9]: 2035

当我切换回Django 2.1.7时,所有这些代码都可以正常工作。应用程序中的其他模型对象也会发生同样的情况。我在Windows上使用的是python 3.7.2版,带有SQlite后端(在开发工作站上)

编辑:以下是上述模型的定义:

class Station(models.Model):
    nom = models.CharField(max_length=200, unique=True)

    def __str__(self):
        return self.nom

    def __repr__(self):
        return "<Station(nom='{}')>".format(self.nom)

    def __eq__(self, other):
        return isinstance(other, Station) and self.nom == other.nom

Tags: thein模型selffalsereturnobjectsmodels
1条回答
网友
1楼 · 发布于 2024-06-13 10:24:05

正如@Alasdair所指出的,问题是Django 2.2中引入的行为改变,以符合当__eq__()被重写而不是__hash__()时模型类应该如何行为。根据{}的{a1}:

A class that overrides __eq__() and does not define __hash__() will have its __hash__() implicitly set to None.

有关在Django中实现此行为的更多信息,请参见this ticket

修复可以是票证中建议的修复,即将模型的__hash__()方法重新分配给超类中的一个: __hash__ = models.Model.__hash__

或者更面向对象的方法可以是:

    def __hash__(self):
        return super().__hash__()

这似乎有点奇怪,因为这应该是不必要的:默认情况下,对__hash__()的调用应该使用实现它的超类中的方法。这表明Django以某种方式破坏了封装。但也许我不是什么都懂。不管怎么说,这是个旁注

在我的例子中,我仍然希望能够比较尚未保存到数据库中用于测试的模型实例,最终实现如下:

    def __hash__(self):
        if self.pk is None:
            return hash(self.nom)
        return super().__hash__()

相关问题 更多 >