views:

237

answers:

3

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