pythondjangodjango-modelsdjango-templatesdjango-model-field

How to pass concatenated functions as a argument in django model method


I am trying to pass timezone.now() + timedelta(days=-2) and timezone.now() as arguments in django model method but am getting

def performance(user, timezone.now() + timedelta(days=-2), timezone.now()):
                                  ^
SyntaxError: invalid syntax

I know the error is as a result of the concatenation but I dont know how to solve this problem.

class User(AbstractUser):
    .........................
    fields
    .........................

    def get_performance(self, timezone.now() + timedelta(days=-2), timezone.now()):

        actual = Sum("scores", filter=Q(status="completed"))

        q = self.taskassignt.filter(
            due__gte=timezone.now() + timedelta(days=-2),
            due__lt=timezone.now() 
        ).annotate(actual=actual, total=Sum("scores"))

        return (q[0].actual / q[0].total) * 100

Solution

  • You need to specify a parameter name, so

    def get_performance(self, start_time=timezone.now() + timedelta(days=-2), end_time=timezone.now()):
        actual = Sum("scores", filter=Q(status="completed"))
        q = self.taskassignt.filter(
            due__gte=start_time,
            due__lt=end_time 
        ).annotate(actual=actual, total=Sum("scores"))
    
        return 100 * q[0].actual / q[0].total

    But this will not work, since the default value will be initialized once and then be used each time. That means that if your server is running for an entire year, start_time will still be the time from th previous year.

    Usually one works with a None as default value, and if the parameter is indeed None, substitute it with a given expression, so:

    def get_performance(self, start_time=None, end_time=None):
        if start_time is None:
            start_time = timezone.now() + timedelta(days=-2)
        if end_time is None:
            end_time = timezone.now()
        actual = Sum("scores", filter=Q(status="completed"))
        q = self.taskassignt.filter(
            due__gte=start_time,
            due__lt=end_time 
        ).annotate(actual=actual, total=Sum("scores"))
    
        return 100 * q[0].actual / q[0].total

    It is however odd to have a 100 * q[0].actual / q[0].total. This means that you are only intersted in the first taskassignt, not in all. Likely you want to .aggregate(…) [Django-doc] instead of .annotate(…) [Django-doc]:

    def get_performance(self, start_time=None, end_time=None):
        if start_time is None:
            start_time = timezone.now() + timedelta(days=-2)
        if end_time is None:
            end_time = timezone.now()
        actual = Sum("scores", filter=Q(status='completed'))
        q = self.taskassignt.filter(
            due__gte=start_time,
            due__lt=end_time 
        ).aggregate(actual=actual, total=Sum("scores"))
    
        return 100 * q['actual'] / q['total']