I have a model that has an ordering field under its Meta class. When I perform a query and get back a QuerySet for the model it is in the order specified. However if I have instances of this model that are in a list and execute the sort method on the list the order is different from the one I want. Is there a way to sort a list of instances of a model such that the order is equal to that specified in the model definition?
+3
A:
The answer to your question is varying degrees of yes, with some manual requirements. If by list
you mean a queryset
that has been formed by some complicated query, then, sure:
queryset.order_by(ClassName.Meta.ordering)
or
queryset.order_by(instance._meta.ordering)
or
queryset.order_by("fieldname") #If you like being manual
If you're not working with a queryset, then of course you can still sort, the same way anyone sorts complex objects in python:
- Comparators
- Specifying keys
- Decorate/Sort/Undecorate
See the python wiki for a detailed explanation of all three.
David Berger
2009-04-15 21:23:23
+2
A:
Not automatically, but with a bit of work, yes. You need to define a comparator function (or cmp method on the model class) that can compare two model instances according to the relevant attribute. For instance:
class Dated(models.Model):
...
created = models.DateTimeField(default=datetime.now)
class Meta:
ordering = ('created',)
def __cmp__(self, other):
try:
return cmp(self.created, other.created)
except AttributeError:
return cmp(self.created, other)
Carl Meyer
2009-04-16 13:31:44
+1 nice solution that keeps the sorting at the model level so it can be transparently honoured no matter where the list sorting is done.
Jarret Hardie
2009-04-16 13:54:58
A:
Building on Carl's answer, you could easily add the ability to use all the ordering fields and even detect the ones that are in reverse order.
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birthday = date = models.DateField()
class Meta:
ordering = ['last_name', 'first_name']
def __cmp__(self, other):
for order in self._meta.ordering:
if order.startswith('-'):
order = order[1:]
mode = -1
else:
mode = 1
if hasattr(self, order) and hasattr(other, order):
result = mode * cmp(getattr(self, order), getattr(other, order))
if result: return result
return 0
mattkemp
2009-04-16 22:17:51