managers.py 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940
  1. from django.db.models import Manager
  2. NAT1 = r"CAST(SUBSTRING({}.{} FROM '^(\d{{1,9}})') AS integer)"
  3. NAT2 = r"SUBSTRING({}.{} FROM '^\d*(.*?)\d*$')"
  4. NAT3 = r"CAST(SUBSTRING({}.{} FROM '(\d{{1,9}})$') AS integer)"
  5. class NaturalOrderingManager(Manager):
  6. """
  7. Order objects naturally by a designated field (defaults to 'name'). Leading and/or trailing digits of values within
  8. this field will be cast as independent integers and sorted accordingly. For example, "Foo2" will be ordered before
  9. "Foo10", even though the digit 1 is normally ordered before the digit 2.
  10. """
  11. natural_order_field = 'name'
  12. def get_queryset(self):
  13. queryset = super(NaturalOrderingManager, self).get_queryset()
  14. db_table = self.model._meta.db_table
  15. db_field = self.natural_order_field
  16. # Append the three subfields derived from the designated natural ordering field
  17. queryset = queryset.extra(select={
  18. '_nat1': NAT1.format(db_table, db_field),
  19. '_nat2': NAT2.format(db_table, db_field),
  20. '_nat3': NAT3.format(db_table, db_field),
  21. })
  22. # Replace any instance of the designated natural ordering field with its three subfields
  23. ordering = []
  24. for field in self.model._meta.ordering:
  25. if field == self.natural_order_field:
  26. ordering.append('_nat1')
  27. ordering.append('_nat2')
  28. ordering.append('_nat3')
  29. else:
  30. ordering.append(field)
  31. return queryset.order_by(*ordering)