python - 升级到 django 2.2 后,Django 模型对象变得不可散列

标签 python django django-2.2

我正在测试从 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'

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

我不明白错误来自哪里以及为什么我在运行这个基本代码时会得到这个:
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

最佳答案

正如@Alasdair 所指出的,问题是 Django 2.2 中的行为改变,以符合模型类在 __eq__() 时的行为方式。被覆盖但未被覆盖 __hash__() .根据 python docs__hash__() :

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



有关在 Django 中实现此行为的更多信息可以在 this ticket 中找到。 .

修复可以是故障单中建议的修复,即重新分配 __hash__()模型到父类(super class)之一的方法:__hash__ = models.Model.__hash__
或者更面向对象的方式可能是:
    def __hash__(self):
        return super().__hash__()

这看起来有点奇怪,因为这应该是不必要的:默认情况下,调用 __hash__()应该使用实现它的父类(super class)中的方法。这表明 Django 以某种方式破坏了封装。但也许我不明白一切。无论如何,这是一个旁注。

就我而言,我仍然希望能够比较尚未保存到数据库中以进行测试的模型实例,并最终得到了以下实现:
    def __hash__(self):
        if self.pk is None:
            return hash(self.nom)
        return super().__hash__()

关于python - 升级到 django 2.2 后,Django 模型对象变得不可散列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61212514/

相关文章:

具有大列表的 Python 结构

python - 在Python中通过电子邮件发送图像

python - Django:在 blocktrans 中使用 if-else block 的 firstof

python - Django - 如果在给定的查询列表中找不到记录,则返回无对象

python - 通过某个 "type"(属性)的最新记录提高QuerySet过滤器的效率

python - 用完 cron.hourly 不会导入 Python 模块

python - 将文本标记标签添加到 pcolor 热图

python - 如何使用 Python 测试网页是否在线

django-2.2 - 如何在Django中的特定时间后自动删除数据库中的记录

python - 如何正确执行ManyToMany字段的django查询