2

I have two models - a Task model, and a Worker model. I have a property on Worker that counts how many tasks they have completed this month.

class Task(models.Model):
    # ...
    completed_on = models.DateField()

class Worker(models.Model):
    # ...
    @property
    def completed_this_month(self):
        year = datetime.date.today().year
        month = datetime.date.today().month
        return Task.objects.filter(worker=self,
                                   completed_on__year=year,
                                   completed_on__month=month).count()

I've added this field to the Worker admin, and it displays correctly.

I would like to be able to sort by this field. Is there a way to do this?

Edit: It has been suggested that my question is a duplicate of this question, which uses extra(). The Django documentation strongly advises against using the extra() method, and even asks you to file a ticket explaining why you had to use it.

Use this method as a last resort

This is an old API that we aim to deprecate at some point in the future. Use it only if you cannot express your query using other queryset methods. If you do need to use it, please file a ticket using the QuerySet.extra keyword with your use case (please check the list of existing tickets first) so that we can enhance the QuerySet API to allow removing extra().

Community
  • 1
  • 1
Greg Kaleka
  • 1,942
  • 1
  • 15
  • 32
  • Possible duplicate of [Sorting a Django QuerySet by a property (not a field) of the Model](https://stackoverflow.com/questions/4175749/sorting-a-django-queryset-by-a-property-not-a-field-of-the-model) – Paulo Almeida Mar 24 '18 at 18:09
  • 2
    The duplicate target uses `extra()` which is not recommended any more. – Alasdair Mar 24 '18 at 18:15
  • @Alasdair It also suggests sorting. I saw [a different duplicate](https://stackoverflow.com/questions/8478494/ordering-django-queryset-by-an-property) that pointed to the one I linked and just suggests sorting. I didn't use it because it wasn't the "root" duplicate, but perhaps it's cleaner. – Paulo Almeida Mar 24 '18 at 18:22
  • You probably need to figure out how to generate aggregates for each item in a QuerySet (https://docs.djangoproject.com/en/dev/topics/db/aggregation/#generating-aggregates-for-each-item-in-a-queryset). Or you could create a view with a group by and attach a model to it... – thebjorn Mar 24 '18 at 18:23
  • 1
    ps: there's a race-condition in your year/month calculation, better get year and month from the same call to `today()`. – thebjorn Mar 24 '18 at 18:25
  • @PauloAlmeida yes, it also mentions sorting the list in Python, but you can’t do that with the Django admin. – Alasdair Mar 24 '18 at 19:28
  • @gregkaleka instead of `extra()` you should look at `annotate()`. For your specific question I think that you might be able to do it using the subquery functionality added in Django 1.11. – Alasdair Mar 24 '18 at 19:40
  • @Alasdair Ok, that's a good reason for it not being a duplicate of those questions. Greg, [query expressions](https://docs.djangoproject.com/en/2.0/ref/models/expressions/), using [TruncMonth](https://docs.djangoproject.com/en/2.0/ref/models/database-functions/#django.db.models.functions.TruncMonth) and [Count](https://docs.djangoproject.com/en/2.0/ref/models/querysets/#django.db.models.Count) may help you, but it's not immediately obvious to me. – Paulo Almeida Mar 24 '18 at 19:46

0 Answers0