Viktor
Viktor

Reputation: 489

'datetime.date' object is not callable

I have a model with a date field and a following method:

class Pm(models.Model):
    mydate = models.DateField()

    @property
    def due_date(self):
        today = timezone.localtime(timezone.now()).date()
        if self.mydate < today:
            return today
        else:
            return self.mydate

How can I order myModel.objects.all() based on the return of due_date function? I tried:

qs = sorted(Pm.objects.all(), key=lambda a: a.due_date(), reverse=True)

but it returns error:

'datetime.date' object is not callable

UPDATE Based on Sami's suggestion:

@staticmethod
    def due_date(obj):
        today = timezone.localtime(timezone.now()).date()
        if obj.pm_date < today:
            return today
        else:
            return obj.pm_date

qs = sorted(Pm.objects.all(), key=Pm.due_date, reverse=True)

Seems to work, except it returns a list instead of a queryset.

What method should I use to get a queryset?

Upvotes: 1

Views: 690

Answers (2)

user1600649
user1600649

Reputation:

Honestly, I wouldn't sort it in python. For two reasons:

  • Database should do it better/faster than python
  • The property makes a lot of dates not sortable: everything that is older than today is made equal. It serves no purpose, especially without a secondary sort field.

And since you ultimately want a queryset, just keep it simple:

qs = Pm.objects.order_by('-mydate')
# Now we can display the due date property when we render things:
for pm in qs:
   print(pm.due_date)

Upvotes: 2

Sami
Sami

Reputation: 381

I think you need to make your method due_date a static method or separate it from model, method gets the obj and return a date, and then use this method in sorted method. Something like this

def due_date(obj):
        today = timezone.localtime(timezone.now()).date()
        if obj.date < today:
            return today
        else:
            return obj.date
qs = sorted(Pm.objects.all(), key=due_date, reverse=True)

If you make this method static in the Model class, then use it as:

qs = sorted(Pm.objects.all(), key=PM.due_date, reverse=True)

Otherwise you need to annotate and then use order_by of the queryset. annotation with conditional expressions F-expressions

something like this (it may not be the exact solution but you need to do something like this):

from django.db.models import Case, DateField, Value, When
qs = Pm.objects.annotate(
    due_date=Case(
        When(date__lt=today, then=Value(today)),
        default=F("date"),
        output_field=DateField(),
    )
)
qs.order_by("due_date")

Upvotes: 2

Related Questions