djangodjango-filter

Django 'When' object is not iterable


I'm using django-filter to show work-hour of employs.

models.py:

    ...
    class Attendance(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Attendances")
        day = models.DateField()
        start_time = models.TimeField()
        end_time = models.TimeField()
        is_confirmed = models.BooleanField()
        task = models.ForeignKey(Tasks, on_delete=models.CASCADE)
        
     class Task(models.Model):
        title = models.CharField(max_length=255)
        project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="Tasks")
        assignee = models.ForeignKey(User, on_delete=models.CASCADE)
       
    ...

filter.py:

class WorkhourFilter(django_filters.FilterSet):
    user = django_filters.ModelChoiceFilter(queryset=User.objects.filter(is_superuser=False),field_name="user")
    project = django_filters.ModelChoiceFilter(queryset=Project.objects.all(), field_name="task__project")
    year = django_filters.ChoiceFilter(choices=tuple((x, x) for x in settings.YEARS_LIST),method='year_search')
    month = django_filters.ChoiceFilter(choices=tuple((i, x) for i, x in enumerate(settings.MONTH_LIST)),method='month_search' )
    
    class Meta:
    model = Attendance
    fields = ['user']
        
    def __init__(self, *args, **kwargs):
         super(WorkhourFilter, self).__init__(*args, **kwargs)
         start_date = ...
         end_date = ...
         self.queryset = self.Meta.model.objects.filter(user=self.request.user, 
         day__gte=start_date, day__lte=end_date).values('task__project__title', 
         'task__project').annotate(
          raw_work_hour=Sum(F('end_time') - F('start_time'), ),
          confirmed_work_hour=Sum(Case(When(is_verified=1, 
          then=F('end_time') - 
          F('start_time'))))).order_by('task__project')

I get 'When' object is not iterable error.

confirmed_work_hour has problem but the raw_work_hour alone is not a problem.

This query is working well in get_queryset() of corresponding View but when I trying to apply filters it'll break.

trace :

Django Version: 4.2.5
Python Version: 3.9.13
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django_extensions',
 'crispy_forms',
 'crispy_bootstrap5',
 'django_tables2',
 'django_htmx',
 'django_filters',
 'ckeditor',
 'ckeditor_uploader',
 'guardian',
 'django_cascading_dropdown_widget',
 'dashboard.apps.DashboardsConfig',
 'auth.apps.AuthConfig']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django_htmx.middleware.HtmxMiddleware']



Traceback (most recent call last):
  File "D:\Python\DjangoTest\venv\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
  File "D:\Python\DjangoTest\venv\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\Python\DjangoTest\venv\lib\site-packages\django\views\generic\base.py", line 104, in view
    return self.dispatch(request, *args, **kwargs)
  File "D:\Python\DjangoTest\venv\lib\site-packages\django\contrib\auth\mixins.py", line 73, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "D:\Python\DjangoTest\venv\lib\site-packages\braces\views\_access.py", line 438, in dispatch
    return super(StaffuserRequiredMixin, self).dispatch(
  File "D:\Python\DjangoTest\venv\lib\site-packages\braces\views\_access.py", line 375, in dispatch
    return super(GroupRequiredMixin, self).dispatch(
  File "D:\Python\DjangoTest\venv\lib\site-packages\django\views\generic\base.py", line 143, in dispatch
    return handler(request, *args, **kwargs)
  File "D:\Python\DjangoTest\venv\lib\site-packages\django_filters\views.py", line 74, in get
    self.filterset = self.get_filterset(filterset_class)
  File "D:\Python\DjangoTest\venv\lib\site-packages\django_filters\views.py", line 38, in get_filterset
    return filterset_class(**kwargs)
  File "D:\Python\DjangoTest\dashboard\filters\workhour_project.py", line 71, in __init__
    confirmed_work_hour=Sum(Case(When(is_verified=1, then=F('end_time') - F('start_time')))),
  File "D:\Python\DjangoTest\venv\lib\site-packages\sqlparse\sql.py", line 160, in __init__
    [setattr(token, 'parent', self) for token in self.tokens]

Exception Type: TypeError at /report/workhour/by_project
Exception Value: 'When' object is not iterable

Solution

  • Thanks to all friends who took the time to solve my problem.

    Looking again at the error trace in the last lines, I noticed a wrong import. Of course, last comment of @willeM_VanOnsem forced me to look at imports again and I am grateful to him.

    The Case command was mistakenly imported from sqlparse.sql when it should have been imported from django.db.models. By applying the above change, the code worked smoothly.

    The desired line in the trace that pointed to the sqlparse package:

    File "D:\Python\DjangoTest\venv\lib\site-packages\sqlparse\sql.py", line 160, in __init__
        [setattr(token, 'parent', self) for token in self.tokens]