Walucas
Walucas

Reputation: 2568

Django ListView with filter option, like DjangoAdmin

I am trying to implement a ListView with the filter capability like the one in Django Admin. I looked into django-filter, but its not clear how to use that on my template.

This is my ListView:

@method_decorator(login_required, name='dispatch')
class ListProjects(ListView):    
    model = Project
    paginate_by = 100
    list_filter=('start_dt','end_dt','status')

In this case I would like to filter by those three fields.

Upvotes: 2

Views: 826

Answers (2)

Walucas
Walucas

Reputation: 2568

So I made a hybrid between my custom view and the admin. Here is the final solution:

 class ListProjects(ListView):    
    model = Project
    paginate_by = 100
    adm_model = ProjectAdmin(Project,AdminSite()) 
    changelist = None        

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        self.changelist = self.adm_model.get_changelist_instance(self.request)

        context['cl']=self.changelist 
        return context

    def get_queryset(self):
        qs = super().get_queryset()
        get_params = self.request.GET.dict()

        self.changelist = self.adm_model.get_changelist_instance(self.request)

        (self.changelist.filter_specs, self.changelist.has_filters, remaining_lookup_params,
         filters_use_distinct) = self.changelist.get_filters(self.request)

        # Then, we let every list filter modify the queryset to its liking.
        qs = self.changelist.root_queryset
        for filter_spec in self.changelist.filter_specs:
            new_qs = filter_spec.queryset(self.request, qs)
            if new_qs is not None:
                qs = new_qs
        try:
            qs = qs.filter(**remaining_lookup_params)
        except:
            pass

        # Set ordering.
        ordering = self.changelist.get_ordering(self.request, qs)
        qs = qs.order_by(*ordering)

        # Apply search results
        qs, search_use_distinct = self.changelist.model_admin.get_search_results(self.request, qs, self.changelist.query)

        return qs

In the template, I have for search piece

    <div class="row-fluid">
    <div class="span12">
        <form class='form-inline' accept-charset='UTF-8' method='get' action=''>
            <input type="text" size="40" name="q" value = "{{cl.params.q}}" id="searchbar" autofocus="">

            <button type='submit' class="btn">Search</button>
        </form>
    </div>
</div>

filter piece

        {% if cl.has_filters %}
          <div id="changelist-filter">
            <h3>{% trans 'Filter' %}</h3>
            {% for spec in cl.filter_specs %}
            {% admin_list_filter cl spec %}
            </div>
            {% endfor %}
          </div>
        {% endif %}

Upvotes: 2

not2acoder
not2acoder

Reputation: 1152

Django admin filters works by filtering the queryset based on the GET params passed depending on the chosen filter. Having said that, you can easily achieve the same by overriding the get_queryset method of the ListView and doing something like :

def get_queryset(self):
    qs = super().get_queryset()
    get_params = self.request.GET.dict()

    # search
    if get_params.get('q') and hasattr(self, 'search_fields'):
        # logic for search goes here....

    # filter
    if hasattr(self, 'list_filter'):
        filter_fields = self.list_filter
        for key, value in get_params.items():
            if key in filter_fields and key != 'q' and value != '':
                qs = qs.filter(**{key:value})
    return qs

Now in the template you can render the choices for the filters with a tags just like in admin. Make sure to preserve the get params while using this method.

Hope this helps.

Upvotes: 0

Related Questions