3

I'm using django-tables2 and django-filter to list employees from a model. I'm having trouble filtering the initial table rendered. It's including all records. I want the initial table to only include employees where status=1. Then, have the filter take over.

views.py

from .models import Employee
from .tables import EmployeeTable
from .filters import EmployeeFilter
from .forms import EmployeeSearchForm

class EmployeeListView(SingleTableMixin, FilterView):
    model = Employee
    table_class = EmployeeTable
    template_name = 'employees/employee_list.html'

    filterset_class = EmployeeFilter

    def get_table_data(self):
        return Employee.objects.filter(status=1)

filters.py:

from django.db.models import Value, Q
from django import forms

from .models import Employee

class EmployeeFilter(django_filters.FilterSet):
    search = django_filters.CharFilter(label='Search', method='search_filter')
    inactive = django_filters.BooleanFilter(widget=forms.CheckboxInput, label='Inactive', method='include_inactive')
    
    def search_filter(self, queryset, name, value):
        return queryset.annotate(fullname=Concat('first_name', Value(' '), 'last_name')).filter(
                Q(fullname__icontains=value) | Q(g_number__icontains=value)
            )

    def include_inactive(self, queryset, name, value):
        expression = Q(status__in=[1,2,5]) if value else Q(status__in=[1,5])
        return queryset.filter(expression)
    
    class Meta:
        model = Employee
        fields = ['search', 'inactive']

tables.py:

from django.utils.html import format_html
from django.urls import reverse

import django_tables2 as tables

from .models import Employee

class EmployeeTable(tables.Table):
    full_name = tables.Column(order_by=("first_name", "last_name"))
    g_number = tables.Column(linkify=True)
    selection = tables.columns.CheckBoxColumn(
            accessor='pk', 
            attrs = { "th__input": {"onclick": "toggle(this)"}},
            orderable=False
        )
    term = tables.Column(verbose_name=("Action"), empty_values=())

    class Meta:
        model = Employee
        
        template_name = "django_tables2/bootstrap.html"
        fields = ("selection", "g_number","full_name", 'org', 'job', 'status', 'term')
        attrs = {
            "class": "table table-hover",
            "thead": {"class": "thead-dark"}
        }
        order_by = '-g_number'
Shaun Stacey
  • 33
  • 1
  • 3

1 Answers1

1

From the Django filter docs you can filter the primary queryset on the filter class. Update: To set the qs based on an attribute in the self.request, just check for that attribute and filter accordingly.

https://django-filter.readthedocs.io/en/master/guide/usage.html#filtering-the-primary-qs

class EmployeeFilter(django_filters.FilterSet):

    [...]       

    @property
    def qs(self):
        parent = super().qs
        filter_inactive = getattr(self.request, 'inactive', False)

        if not filter_inactive:
            return parent.filter(status=1)

        return parent
Ben
  • 2,348
  • 1
  • 20
  • 23
  • This filters the initial table correctly. However, checking the "inactive" filter doesn't include employees with a status of 2 and 5 as expected. – Shaun Stacey Mar 31 '21 at 23:54
  • So, to be clear, you want initial as only status=1, then upon any search: inactive=False/None returns status=1,5 and inactive=True returns status=1,2,5 ? – Ben Apr 01 '21 at 00:25
  • Yes, that’s exactly what I’m after. Thanks for your help!!! – Shaun Stacey Apr 01 '21 at 11:10