30

I'm trying to order a query set by a property I defined in the model, but not sure the best way to do this. Here's the property:

@property
def name(self):
    if self.custom_name:
        return self.custom_name
    else:
        return self.module_object.name

Essentially, I'd like to do a:

things = Thing.objects.all().order_by('-name')

but of course getting a Caught FieldError while rendering: Cannot resolve keyword 'name' into field.

Any ideas?

EDIT: I understand that I can't sort this way because the @property isn't a database field. My question is how to sort given that @property isn't a database field.

John R Perry
  • 3,916
  • 2
  • 38
  • 62
Brenden
  • 8,264
  • 14
  • 48
  • 78

4 Answers4

44

You can't do that because that property is not in MySQL, but in your python code. If you really want to do this, you can on the client-side(though it will be very slow):

sorted(Thing.objects.all(), key=lambda t: t.name)
Gabi Purcaru
  • 30,940
  • 9
  • 79
  • 95
  • This is almost working, but it's returning odd sorting. Basically, the ones with self.custom_name are sorted and then the ones with self.module_object.name are sorted next. – Brenden Dec 12 '11 at 18:02
  • 1
    nevermind. I just needed to add a .lower() after the name for the case-insensitive sort – Brenden Dec 12 '11 at 23:48
4

order_by happens on the sql level, so it can't make use of properties, only field data.

have a look at the queryset api, you may be able to make use of e.g. extra to annotate your query and sort on that

second
  • 28,029
  • 7
  • 75
  • 76
  • thanks .. I've taken a look at Extra but not sure how to incorporate the @property into it – Brenden Dec 12 '11 at 18:08
  • @brenden, you don't incorporate the property as such. In my example, I needed to have status=DRAFT (where DRAFT='draft') come first. So, I used extra to insert a draft column in the select statement that was true if status=DRAFT (exluding status=DONE) and ordered on it like so: MyModel.objects.exclude(status=DONE).extra(select={'draft': 'status = %s'}, select_params=[DRAFT]).order_by('-draft') – MagicLAMP Jan 15 '18 at 10:23
4

Have a look at django-denorm. It lets you maintain calculated values in the database (which you can then use to sort efficiently) for the cost of a single method decorator.

Evan Brumley
  • 2,468
  • 20
  • 13
3

You can only order by database fields. You can pull all records, turn them into a list, and sort them, although that may be inefficient and slow.

Or you can add a database field called name and fill it in via a post-save hook; then you can sort it using order_by.

mipadi
  • 398,885
  • 90
  • 523
  • 479