I started using django-tables2 (which I can highly recommend from the first impression) and I m asking myself how to implement column filtering. I do not find the appropriate documentation for it, but I m sure it is somewhere out there.
问题:
回答1:
A little late answer but anyway ... I also couldn't find any appropriate documentation for column filtering. There are many methods to do it:
A. By hand: I add a form containing the fields I'd like to filter with and then I do something like this in my view:
data = models.MyClass.all() form = forms.MyFilterForm(request.GET) if request.GET.get('field1'): data = data.filter(field1=request.GET.get('field1') ) if request.GET.get('field2'): data = data.filter(field2=request.GET.get('field2') ) ... table = tables.MyTable(data)
This works very nice however it's not so DRY because it is hard coded in the view.
B. Using a SingleTableView: Another way is to add a SingleTableView that contains the form:
from django_tables2 import SingleTableView class FilteredSingleTableView(SingleTableView): def get_table_data(self): data= models.MyClass.objects.all if self.request.GET.get('field1'): data = data.filter(field1=self.request.GET.get('field1') ) if self.request.GET.get('field1'): data = data.filter(field1=self.request.GET.get('field1') ) return data def get_context_data(self, **kwargs): context = super(FilteredSingleTableView, self).get_context_data(**kwargs) context['form'] = forms.MyFilterForm(self.request.user, self.request.GET) return context
This is more DRY :)
C. Using SingleTableView and django_filters: This probably is the most DRY way :) Here's how to do it:
First define a filter:
class MyFilter(django_filters.FilterSet): field1 = django_filters.CharFilter() field2 = django_filters.CharFilter() ...
(or you can add a model filter in Meta ( model = MyModel)
Now, create a SingleTableView like this
class FilteredSingleTableView(SingleTableView): def get_table_data(self): f = filters.MyFilter(self.request.GET, queryset =models.MyClass.objects.all() , request=self.request ) return f def get_context_data(self, **kwargs): context = super(FilteredSingleTableView, self).get_context_data(**kwargs) f = filters.MyFilter(self.request.GET, queryset =models.MyClass.objects.all() , request=self.request ) context['form'] = f.form return context
(probably there is a problem with the line f =... but I couldn't make it work otherwise.
Finally, you can call the SingleTableView from your urls.py like this
url(r'^$', views.FilteredSingleTableView.as_view( table_class = tables.MyTable, model=models.MyClass, template_name ='mytemplate.html', table_pagination={ "per_page":50 } )) , name='filtered_single_table_view' ),
D. Using a generic class: This is an even more DRY and django-generic-class-views like way! This is actually the next step from C: Just declare your FilteredSingleTableView like this:
class FilteredSingleTableView(django_tables2.SingleTableView): filter_class = None def get_table_data(self): self.filter = self.filter_class(self.request.GET, queryset =super(FilteredSingleTableView, self).get_table_data() ) return self.filter.qs def get_context_data(self, **kwargs): context = super(FilteredSingleTableView, self).get_context_data(**kwargs) context['filter'] = self.filter return context
Now the FilteredSingleTableView has a parameter for the class of the filter so you may pass it in your urls.py among the other parameters:
url(r'^$', ships.views.FilteredSingleTableView.as_view( model=models.MyModel, table_class=tables.MyTable, template_name='mytemplate.html' , filter_class = filters.MyFilter, ) , name='myview'),
So you can use FilteredSingleTableView without modifications for filtering any of your models !!
Also notice that I've now saved the filter as an instance variable and removed the repetitive code f=filters.MyFilter(...)
that I had in C (get_table_data is called before get_context_data - if that was not always the case then we could add an get_filter
instance method that would do the trick) !
Update 23/04/2016: After popular demand, I've created a simple Django project that uses the generic FilteredSingleTableView class to filter a table of books. You may find it out at: https://github.com/spapas/django_table_filtering
Update 05/07/2016: Please notice that you should use return self.filter.qs
for the get_table_data
return in D (I've alread updated the answer with this) or else the view will take too long to render for big tables -- more info can be found on https://github.com/spapas/django_table_filtering/issues/1
回答2:
There is an easier and DRYer way to build a generic view do this:
from django_filters.views import FilterView
from django_tables2 import SingleTableView
class FilterTableView(FilterView, SingleTableView):
def get_table_data(self):
return self.object_list
So you can do this:
class MyTableView(FilterTableView):
model = MyModel
table_class = MyTable
filterset_class = MyFilter
回答3:
If you prefer to use django_tables2.views.SingleTableMixin
in concert with Django's ListView
or a subclass thereof (rather than SingleTableView
) I suggest the following:
class FilteredListViewMixin(object):
""" Uses django-filter to filter a ListView. """
filter_class = None
def get_queryset(self):
qs = super(FilteredListViewMixin, self).get_queryset()
self.filter = self.filter_class(self.request.GET,
queryset=qs)
return self.filter.qs
def get_context_data(self, **kwargs):
context = super(FilteredListViewMixin, self).get_context_data(**kwargs)
context['filter'] = self.filter
return context
It has the added benefit of not being coupled to django-tables2
(DRY FTW) meaning it can be used with generic ListViews
also.