Django: TypeError: '<' not supported between instances (model objects)

此生再无相见时 提交于 2019-12-01 09:42:25

问题


I'm trying to migrate my Django project from Python 2.7/Django 1.11 to Python 3.7/Django 2.1.

I've found one issue and I want to understand its cause.

I have 3 models in my project:

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 + ")"

And this is my code to sort Device objects by latest test status ('dev_filter', 'field' and 'order' parameters are parsed from GET request):

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()

This code worked fine in Python 2.7/Django 1.11 but it doesn't in Python 3.7/Django 2.1

Django marks as error sorted(zip(dev_status_list, device_list)) function:

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

I see two solutions to this problem: either use

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__()))]

or add __lt__ method to Device model:

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

My question is - what is changed? Does this error happen because of Python upgrade or Django upgrade? What was default sorting method in Python 2.7/Django 1.11 framework for Device objects? Am I correct that it was string representation? And which of my solutions is preferred?


回答1:


Python 3 introduces new ordering comparison:

The ordering comparison operators (<, <=, >=, >) raise a TypeError exception when the operands don’t have a meaningful natural ordering.

A simple example which prints True in Python2 and raises a TypeError in Python3

class A:
    pass

print(A() < A())



回答2:


The reason is because Python 3 has simplified the rules for ordering comparisons which changes the behavior of sorting lists when their contents are dictionaries.
The ordering comparison operators (<, <=, >=, >) raise a TypeError exception when the operands don’t have a meaningful natural ordering

Also there is another interesting example

Quoting the example from the mentioned 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()

The problem is that the second elements in both lists have different keys and Python doesn't know how to compare them. In earlier Python versions this has been special cased as described here by Ned Batchelder (the author of Python's coverage tool) but in Python 3 dictionaries have no natural sort order.

You can read more about the problem here.



来源:https://stackoverflow.com/questions/51946150/django-typeerror-not-supported-between-instances-model-objects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!