Я начал использовать django-tables2 (который я могу очень порекомендовать с первого впечатления), и я задаю себе вопрос, как реализовать фильтрацию столбцов. Я не могу найти соответствующую документацию, но я уверен, что это где-то там.
Таблицы Django - Фильтрация столбцов
Ответ 1
Немного поздний ответ, но в любом случае... Я также не смог найти подходящую документацию для фильтрации столбцов. Для этого есть много способов:
а. Рукой: я добавляю форму, содержащую поля, которые я хотел бы фильтровать, а затем я делаю что-то вроде этого на мой взгляд:
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)
Это работает очень хорошо, однако оно не так сухо, потому что оно жестко закодировано в представлении.
В. Использование SingleTableView. Еще один способ - добавить SingleTableView, который содержит форму:
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
Это более DRY:)
С. Использование SingleTableView и django_filters. Это, вероятно, самый DRY-способ:) Вот как это сделать:
Сначала определите фильтр:
class MyFilter(django_filters.FilterSet): field1 = django_filters.CharFilter() field2 = django_filters.CharFilter() ...
(или вы можете добавить фильтр модели в Meta (model = MyModel)
Теперь создайте SingleTableView следующим образом
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
(возможно, существует проблема с линией f =... но я не мог заставить ее работать иначе.
Наконец, вы можете вызвать SingleTableView с вашего urls.py, как это
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' ),
Д. Использование универсального класса: Это еще более DRY и django-generic-class-views, как путь! Это на самом деле следующий шаг от C: просто объявите свой FilteredSingleTableView следующим образом:
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
Теперь FilteredSingleTableView имеет параметр для класса фильтра, поэтому вы можете передать его в urls.py среди других параметров:
url(r'^$', ships.views.FilteredSingleTableView.as_view( model=models.MyModel, table_class=tables.MyTable, template_name='mytemplate.html' , filter_class = filters.MyFilter, ) , name='myview'),
Таким образом, вы можете использовать FilteredSingleTableView без изменений для фильтрации любой из ваших моделей!
Также обратите внимание, что теперь я сохранил фильтр как переменную экземпляра и удалил повторяющийся код f=filters.MyFilter(...)
, который у меня был в C (get_table_data вызывается до get_context_data), если это не всегда case, то мы могли бы добавить метод экземпляра get_filter
, который бы сделал трюк)!
Обновление 23/04/2016. После популярного спроса я создал простой проект Django, который использует общий класс FilteredSingleTableView для фильтрации таблицы книг. Вы можете узнать это: https://github.com/spapas/django_table_filtering
Обновление 05/07/2016. Обратите внимание, что вы должны использовать return self.filter.qs
для возврата get_table_data
в D (я обновил ответ с помощью это), иначе вид займет слишком много времени для больших таблиц. Более подробную информацию можно найти на https://github.com/spapas/django_table_filtering/issues/1
Ответ 2
Существует более простой способ DRYer для создания общего представления:
from django_filters.views import FilterView
from django_tables2 import SingleTableView
class FilterTableView(FilterView, SingleTableView):
def get_table_data(self):
return self.object_list
Итак, вы можете сделать это:
class MyTableView(FilterTableView):
model = MyModel
table_class = MyTable
filterset_class = MyFilter
Ответ 3
Если вы предпочитаете использовать django_tables2.views.SingleTableMixin
совместно с Django ListView
или его подклассом (а не SingleTableView
), я предлагаю следующее:
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
Он имеет дополнительное преимущество: он не связан с django-tables2
(DRY FTW), что означает, что он также может использоваться с общим ListViews
.
Ответ 4
Это полный рабочий пример приложения, которое я пишу с использованием Django 2, Crispy Forms и Bootstrap 4:
urls.py:
from django.urls import path
from .views import ASTodasView
urlpatterns = [
path("asignatura/todas", ASTodasView.as_view(), name="as-todas"),
]
views.py:
from .filters import AsignaturaListFilter
from .forms import AsignaturaFilterFormHelper
from .models import Asignatura, Calendario
from .tables import AsignaturasTable
from .utils import PagedFilteredTableView
class ASTodasView(PagedFilteredTableView):
filter_class = AsignaturaListFilter
model = Asignatura
table_class = AsignaturasTable
template_name = "asignatura/todas.html"
formhelper_class = AsignaturaFilterFormHelper
def get_queryset(self):
anyo_academico = Calendario.get_anyo_academico_actual()
return Asignatura.objects.filter(anyo_academico=anyo_academico)
filters.py:
import django_filters
from .models import Asignatura
class AsignaturaListFilter(django_filters.FilterSet):
class Meta:
model = Asignatura
fields = {
"nombre_estudio": ["icontains"],
"nombre_centro": ["icontains"],
"asignatura_id": ["exact"],
"nombre_asignatura": ["icontains"],
"cod_grupo_asignatura": ["exact"],
}
order_by = ["asignatura_id"]
forms.py:
from django import forms
from django.utils.translation import gettext_lazy as _
from crispy_forms.bootstrap import FormActions, InlineField
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Div, Fieldset, Layout, Submit
class AsignaturaFilterFormHelper(FormHelper):
# See https://django-crispy-forms.readthedocs.io/en/latest/form_helper.html
form_class = "form form-inline"
form_id = "asignatura-search-form"
form_method = "GET"
form_tag = True
html5_required = True
layout = Layout(
Div(
Fieldset(
"<span class='fa fa-search'></span> " + str(_("Buscar asignatura")),
Div(
InlineField("nombre_estudio__icontains", wrapper_class="col-4"),
InlineField("nombre_centro__icontains", wrapper_class="col-4"),
InlineField("asignatura_id", wrapper_class="col-4"),
InlineField("nombre_asignatura__icontains", wrapper_class="col-4"),
InlineField("cod_grupo_asignatura", wrapper_class="col-4"),
css_class="row",
),
css_class="col-10 border p-3",
),
FormActions(
Submit("submit", _("Filtrar")),
css_class="col-2 text-right align-self-center",
),
css_class="row",
)
)
tables.py:
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from .models import Asignatura
class AsignaturasTable(tables.Table):
class Meta:
attrs = {"class": "table table-striped table-hover cabecera-azul"}
model = Asignatura
fields = (
"nombre_estudio",
"nombre_centro",
"asignatura_id",
"nombre_asignatura",
"cod_grupo_asignatura",
)
empty_text = _(
"No hay ninguna asignatura que satisfaga los criterios de búsqueda."
)
template_name = "django_tables2/bootstrap4.html"
per_page = 20
utils.py:
from django_tables2 import SingleTableView
class PagedFilteredTableView(SingleTableView):
filter_class = None
formhelper_class = None
context_filter_name = "filter"
def get_table_data(self):
self.filter = self.filter_class(
self.request.GET, queryset=super().get_table_data()
)
self.filter.form.helper = self.formhelper_class()
return self.filter.qs
def get_context_data(self, **kwargs):
context = super(PagedFilteredTableView, self).get_context_data(**kwargs)
context[self.context_filter_name] = self.filter
return context
todas.html:
{% extends 'base.html' %}
{% load crispy_forms_tags i18n %}
{% load render_table from django_tables2 %}
{% block title %}{% trans "Todas las asignaturas" %}{% endblock title %}
{% block content %}
<div class="container-blanco">
<h1>{% trans "Todas las asignaturas" %}</h1>
<hr />
<br />
{% crispy filter.form filter.form.helper %}
<br />
{% render_table table %}
</div>
{% endblock content %}
Надеюсь, поможет. Улучшения приветствуются.