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