Render fields conditionally with Django-Filters

Viewed 202

I'm working on my Django SAAS app in which I want to allow the user to have some custom settings, like disable or enable certain filters. For that I'm using django-user-setttings combined with django-filters and simple forms with boolean fields:

class PropertyFilterSetting(forms.Form):
    filter_by_loans = forms.BooleanField(required=False)
    filter_by_tenants = forms.BooleanField(required=False)

The issue is that when trying to apply those settings, I keep running into serious spaghetti code:

views.py

class PropertyListView(LoginRequiredMixin, FilterView):
    template_name = 'app/property_list.html'
    context_object_name = 'properties'

    def get_filterset_class(self):
        print(get_user_setting('filter_by_tenants', request=self.request))
        return PropertyFilterWithoutTenant if not get_user_setting('filter_by_tenants', request=self.request)['value'] else PropertyFilter

filter.py

class PropertyFilter(django_filter.FilterSet):
    ...
class PropertyFilterWithoutTenant(PropertyFilter):
    ...

and I'd have to do the same thing with the rest of the features. Is there any better way to implement this?

2 Answers

You can create methods in your User model, or a new class which acts as a store for all the methods. Each method will give you the relevant filterset class based on the value of corresponding user setting.

Something like:

class UserFilterset:
    def __init__(self, request):
        self.request = request
    
    def get_property_filterset(self):
        if not get_user_setting('filter_by_tenants', request=self.request)['value']:
            return PropertyFilterWithoutTenant
        return PropertyFilter
   
    ... # add more such methods for each user setting

Now you can use this method to get the relevant filterset class

class PropertyListView(LoginRequiredMixin, FilterView):
    template_name = 'app/property_list.html'
    context_object_name = 'properties'

    def get_filterset_class(self):
        return UserFilterset(self.request).get_property_filterset()

So even if in future you want to add some more logic, you can just update the relevant method, it would be cleaner and manageable.

I'm not sure how MVT stucture will exactly respond to this one but i use a custom generic class in REST structure to add custom filter fields in any viewset that i want

class ListAPIViewWithFilter(ListAPIView):
    def get_kwargs_for_filtering(self):

        filtering_kwargs = {}
        if self.my_filter_fields is not []:
            # iterate over the filter fields
            for field in self.my_filter_fields:
                # get the value of a field from request query parameter
                field_value = self.request.query_params.get(field)
                if field_value:
                    filtering_kwargs[field] = field_value
        return filtering_kwargs


    def get_queryset(self):
        queryset = super(ListAPIViewWithFilter, self).get_queryset()
        filtering_kwargs = self.get_kwargs_for_filtering()
        if filtering_kwargs != {}:
            # filter the queryset based on 'filtering_kwargs'
            queryset = queryset.filter(**filtering_kwargs)
            self.pagination_class = None
        else:
            return queryset
        return queryset[:self.filter_results_number_limit]

changing origional get_queryset function in views.py should be the key to solving your problem (it works in django rest). try checking what function gets the data then just identify the field wanted from it.

Related