Search code examples
pythondjangodjango-filterdjango-tables2

CBV using django-filter with django-table2


I want to use django-table2 with django-filter. I done what django-table2 document said but form of django-filter didn't display in my template.

These are my codes:

    # Views.py 
    from django_filters.views import FilterView
    import django_filters
    from django_tables2 import SingleTableView
    
    class MYFilter(django_filters.FilterSet):
        class Meta:
            model = MyModel
            fields = ['field1', 'field2' ]
    
    class MyView(SingleTableView, FilterView):
         model = MyModel
         table_class = MyTable
         template_name = 'my_template.html'
         filterset_class = MYFilter
    
    # tables.py 
    import django_tables2 as tables
    class MyTable(tables.Table):
        class Meta:
            model = MyModel
            fields = ("field1", "field2",)
    
    # my_template.html
    {% load render_table from django_tables2 %}
    {% if filter %}
    <form method="get">
        {{ filter.form.as_p }}
        <input type="submit" />
    </form>
    {% endif %}
    {% if table %}
    <div class="table-responsive">
        {% render_table table %}
    </div>
    {% endif %}

The table render correctly, but the form didn't render. What's wrong with my code?


Solution

  • The method resolution order (MRO) will put the filter view that low that it has no impact, indeed:

    >>> MyView.__mro__
    (<class 'MyView'>,
     <class 'django_tables2.views.SingleTableView'>,
     <class 'django_tables2.views.SingleTableMixin'>,
     <class 'django_tables2.views.TableMixinBase'>,
     <class 'django.views.generic.list.ListView'>,
     <class 'django_filters.views.FilterView'>,
     <class 'django.views.generic.list.MultipleObjectTemplateResponseMixin'>,
     <class 'django.views.generic.base.TemplateResponseMixin'>,
     <class 'django.views.generic.list.BaseListView'>,
     <class 'django_filters.views.BaseFilterView'>,
     <class 'django_filters.views.FilterMixin'>,
     <class 'django.views.generic.list.MultipleObjectMixin'>,
     <class 'django.views.generic.base.ContextMixin'>,
     <class 'django.views.generic.base.View'>,
     <class 'object'>)

    This is important since the BaseFilterView will override the get method, but since the BaseListView appears first in the MRO, it will not have any impact. If we swap the parents, so:

    class MyView(FilterView, SingleTableView):
        # …

    the mro is:

    >>> MyView.__mro__
    (<class 'MyView'>,
     <class 'django_filters.views.FilterView'>,
     <class 'django_tables2.views.SingleTableView'>,
     <class 'django_tables2.views.SingleTableMixin'>,
     <class 'django_tables2.views.TableMixinBase'>,
     <class 'django.views.generic.list.ListView'>,
     <class 'django.views.generic.list.MultipleObjectTemplateResponseMixin'>,
     <class 'django.views.generic.base.TemplateResponseMixin'>,
     <class 'django_filters.views.BaseFilterView'>,
     <class 'django_filters.views.FilterMixin'>,
     <class 'django.views.generic.list.BaseListView'>,
     <class 'django.views.generic.list.MultipleObjectMixin'>,
     <class 'django.views.generic.base.ContextMixin'>,
     <class 'django.views.generic.base.View'>,
     <class 'object'>)

    and thus the get logic of the BaseFilterView will be used.