【问题标题】:Django: TypeError: '<' not supported between instances (model objects)Django:TypeError:实例(模型对象)之间不支持'<'
【发布时间】:2018-08-21 09:58:31
【问题描述】:

我正在尝试将我的 Django 项目从 Python 2.7/Django 1.11 迁移到 Python 3.7/Django 2.1。

我发现了一个问题,我想了解它的原因。

我的项目中有 3 个模型:

class DeviceModel(models.Model):
    name = models.CharField(max_length=255)
    pirsh = models.CharField(max_length=255)

    def __str__(self):
        return self.name + " - " + self.pirsh

class Device(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    device_model = models.ForeignKey(DeviceModel, on_delete=models.CASCADE)
    serial_number = models.CharField(max_length=255)


    def __str__(self):
        return self.device_model.name + " - " + self.device_model.pirsh + " - " \
                + self.serial_number

class DeviceTest(models.Model):
    device = models.ForeignKey(Device, on_delete=models.CASCADE)
    created_at = models.DateTimeField()

    TEST_OK = '+'
    TEST_ERROR = '-'
    TEST_PENDING = '?'
    TEST_RESULT_CHOICES = (
        (TEST_OK, 'Success'),
        (TEST_ERROR, 'Fail'),
        (TEST_PENDING, 'Not checked'),
    )
    status = models.CharField(max_length=1, choices=TEST_RESULT_CHOICES, default=TEST_PENDING)

    comment = models.TextField(blank=True, default="")
    tester = models.CharField(max_length=255)
    action = models.CharField(max_length=255)

    def save(self, *args, **kwargs):
        ''' On save, update timestamps '''
        if not self.created_at:
            self.created_at = timezone.now()
        return super(DeviceTest, self).save(*args, **kwargs)

    def __str__(self):
        return  self.device_id.device_model.name + " - " + \
                self.device_id.device_model.pirsh + " - " + \
                self.device_id.serial_number + " - " + \
                str(self.created_at) + " - " + \
                "Result (" + self.status + ")"

这是我根据最新测试状态对 Device 对象进行排序的代码('dev_filter'、'field' 和 'order' 参数是从 GET 请求中解析的):

if (dev_filter!="") and (dev_filter!="-1"):
    device_list = Device.objects.all().filter(device_model = dev_filter)
else:
    device_list = Device.objects.all()

dev_status_list = []
for dev in device_list:
    try:
        dev_status_list.append(DeviceTest.objects.filter(device_id=dev.pk).latest('created_at').status)
    except:
        dev_status_list.append("Not checked")

device_list = [device_list for (dev_status_list, device_list) in sorted(zip(dev_status_list, device_list))]

if (order == '-'):
    device_list.reverse()

此代码在 Python 2.7/Django 1.11 中运行良好,但在 Python 3.7/Django 2.1 中却不行

Django 标记为错误sorted(zip(dev_status_list, device_list)) 函数:

TypeError: '<' not supported between instances of 'Device' and 'Device'

我看到了这个问题的两种解决方案:要么使用

device_list = [device_list for (dev_status_list, device_list) in sorted(zip(dev_status_list, device_list), key=lambda x: (x[0],x[1].__str__()))]

或将__lt__ 方法添加到Device 模型:

def __lt__(self, other):
    return self.__str__() < other.__str__()

我的问题是 - 发生了什么变化?这个错误是因为 Python 升级还是 Django 升级而发生的? Python 2.7/Django 1.11 框架中Device 对象的默认排序方法是什么?我是否正确,它是字符串表示?我更喜欢哪种解决方案?

【问题讨论】:

  • 这里可以使用注解,按照数据库的状态排序。
  • 你的代码在 Python2 中被破坏了。它并没有真正“排序”元素……元素是按 id 排序的,这基本上意味着随机。如果Device 具有自然排序顺序,则定义适当的__lt__ 方法。如果Device 没有单一自然排序(因为有更明智的“自然”排序方式),那么您可以考虑添加一些执行各种排序的@classmethods(并且可能选择用于@987654337 的默认排序) @)。这样你就可以做到sorted(..., key=Device.sort_by_&lt;something&gt;)
  • 这里有几个问题。首先是您不应该获取数据并在 django 中对其进行排序。数据库在那里处理它的能力要好得多。其次,循环和查询会导致性能非常差,您应该为此使用连接。与其尝试对这段代码进行 1:1 的翻译,不如重写它。
  • @WillemVanOnsem 如何使用最新测试状态注释对象?我只知道如何用最晚的时间注释对象。如果你知道,请检查我的问题stackoverflow.com/questions/32611275/…
  • @e4c5 我知道这段代码的性能很差。你能指出我重写它的正确方向吗?我很久以前就问过一个关于如何进行排序的问题stackoverflow.com/questions/32611275/…

标签: python django python-3.x django-2.1


【解决方案1】:

Python 3 引入了新的ordering comparison

当操作数没有有意义的自然排序时,排序比较运算符(=、>)会引发 TypeError 异常。

在 Python2 中打印 True 并在 Python3 中引发 TypeError 的简单示例

class A:
    pass

print(A() < A())

【讨论】:

  • Python 2 会根据什么参数对这些对象进行排序?
  • 文档指出default ordering comparison 给出了一致但任意顺序
【解决方案2】:

原因是Python 3 简化了 rules for ordering 比较,当列表的内容是字典时,这会改变排序列表的行为。
当操作数没有有意义的自然排序时,排序比较运算符(=、>)会引发 TypeError 异常

还有一个有趣的例子

引用上面提到的in this link的例子

Python 2.7

>>> [{'a':1}, {'b':2}] < [{'a':1}, {'b':2, 'c':3}]
True

Python 3.5

>>> [{'a':1}, {'b':2}] < [{'a':1}, {'b':2, 'c':3}]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: dict() < dict()

问题是两个列表中的第二个元素有不同 键和 Python 不知道如何比较它们。在早期的 Python 中 Ned 将其特例化为 described here 的版本 Batchelder(Python 覆盖工具的作者)但在 Python 3 中 字典没有自然的排序顺序。

您可以阅读更多关于该问题的信息here

【讨论】:

  • 情况并非如此,在 python 3 中:dict(a=3) &lt; dict(a=3) 也会崩溃。原因见@ikkuh 的回答。
  • 对。我的回答是部分正确的。我会更新的。
猜你喜欢
  • 2017-09-14
  • 2019-04-14
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
  • 2018-09-08
  • 2018-03-13
  • 2020-07-28
  • 2018-03-06
相关资源
最近更新 更多