简体   繁体   中英

Render fields conditionally with Django-Filters

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 withdjango-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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM