django-tables2/django-filter - 使用过滤器类分页
django-tables2/django-filter - pagination with filterclass
下午好,
我在 django-tables2 中使用自定义的 django-filter 来通过单个输入搜索所有字段。我刚刚注意到,当我搜索时,我丢失了分页菜单。
这是link到过滤代码https://spapas.github.io/2016/09/12/django-split-query/
这是我的过滤器
class SiteFilterEx(django_filters.FilterSet):
ex = django_filters.CharFilter(label='Ex filter', method='filter_ex')
search_fields = ['location', 'bgp_as', 'opening_date','town','postcode']
def filter_ex(self, qs, name, value):
if value:
q_parts = value.split()
# Use a global q_totals
q_totals = Q()
# This part will get us all possible segmantiation of the query parts and put it in the possibilities list
combinatorics = itertools.product([True, False], repeat=len(q_parts) - 1)
possibilities = []
for combination in combinatorics:
i = 0
one_such_combination = [q_parts[i]]
for slab in combination:
i += 1
if not slab: # there is a join
one_such_combination[-1] += ' ' + q_parts[i]
else:
one_such_combination += [q_parts[i]]
possibilities.append(one_such_combination)
# Now, for all possiblities we'll append all the Q objects using OR
for p in possibilities:
list1=self.search_fields
list2=p
perms = [zip(x,list2) for x in itertools.permutations(list1,len(list2))]
for perm in perms:
q_part = Q()
for p in perm:
q_part = q_part & Q(**{p[0]+'__icontains': p[1]})
q_totals = q_totals | q_part
qs = qs.filter(q_totals)
return qs
class Meta:
model = Site
fields = ['ex']
form = SiteFilterForm
在我的模板中我可以使用:
Showing {{ filter.qs.count }} of {{ filter.queryset.count }} records
但默认的 Django-tables 使用
Showing {{ table.page.start_index }} to {{ table.page.end_index }} of {{ table.page.paginator.count }} records
当我过滤时,分页完全消失了。
我想我需要 return 一个同名的分页怎么办?但不确定我需要做什么?
这是来自视图的代码:
class Sites(LoginRequiredMixin, ExportMixin, SingleTableMixin, FilterView):
model = Site
table_class = SiteTable
template_name = "app_settings/table_view.html"
login_url = '/login/'
redirect_field_name = 'redirect_to'
filterset_class = SiteFilterEx
exclude_columns = ("buttons", )
def dispatch(self, *args, **kwargs):
site_type = get_object_or_404(SiteType, pk=self.kwargs['site_type'])
site_state = 'Open' if self.kwargs['state'] else 'Closed'
self.site_type_name = '{} {}s'.format(site_state, site_type.site_type)
self.site_type_icon = 'fa {}'.format(site_type.icon)
return super(Sites, self).dispatch(*args, **kwargs)
def get_queryset(self):
site_type = self.kwargs['site_type']
subnet = Subquery(
DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
active_link=True, \
circuit__decommissioned=False
).values('circuit__name')[:1])
active_circuit = Subquery(
DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
active_link=True, \
circuit__decommissioned=False
).values('circuit__name')[:1])
if site_type:
return super(Sites, self).get_queryset().filter(
is_live=self.kwargs['state'], site_type_id=site_type
).annotate(
active_circuit=active_circuit
).prefetch_related('sitesupernet_set')
else:
return super(Sites, self).get_queryset().filter(
is_live=self.kwargs['state']
).annotate(
active_circuit=active_circuit
).prefetch_related('sitesupernet_set')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['page_icon']=self.site_type_icon
context['page_title']=self.site_type_name
return context
以下内容可能会有所帮助:
https://kuttler.eu/en/post/using-django-tables2-filters-crispy-forms-together/
特别是以下几行:
As SingleTableView inherits from Django's ListView you automatically get a Paginator object in the template context as paginator.
The get_queryset() method was changed to apply the filter and to return the filtered queryset. That filtered data ends up in the table in get_table() and gets paged. Aftert that it is added to the template context together with the filter.
我在我的项目中使用它来显示过滤的、分页的 table,使用以下内容作为我的视图的基础 class:
https://gist.github.com/craigderington/0a7ded018b8401bc42822b2eeaaff1cd
像这样:
class PagedFilteredTableView(SingleTableView):
filter_class = None
formhelper_class = None
context_filter_name = 'filter'
paginator_class = LazyPaginator
def get_queryset(self, **kwargs):
qs = super(PagedFilteredTableView, self).get_queryset()
self.filter = self.filter_class(self.request.GET, queryset=qs)
self.filter.form.helper = self.formhelper_class()
return self.filter.qs
def get_context_data(self, **kwargs):
context = super(PagedFilteredTableView, self).get_context_data()
context[self.context_filter_name] = self.filter
return context
我认为class:
class JobList(LoginRequiredMixin, PermissionRequiredMixin, ExportMixin, PagedFilteredTableView):
permission_required = 'jobs.view_job'
template_name = 'jobs/job_list.html'
model = Job
context_object_name = 'job_list'
table_class = JobTable
table_pagination = {'per_page': 20}
filter_class = JobListFilter
formhelper_class = forms.JobListFilterFormHelper
# Columns to exclude in the export
exclude_columns = ('duration', 'job_type', 'contact_name', 'job_status',)
def get_queryset(self):
qs = super(JobList, self).get_queryset()
...
self.filter.form.helper.add_input(
Submit('submit', 'Filter',
css_class='btn pmd-ripple-effect btn-lg btn-primary btn-block',
style='margin-top:10px;')
)
return qs
def get_context_data(self, **kwargs):
context = super(JobList, self).get_context_data(**kwargs)
search_query = self.get_queryset()
table = JobTable(search_query)
RequestConfig(self.request).configure(table)
context['table'] = table
return context
因此,根据您的观点,您可以将 PagedFilteredTableView
class 添加到 views.py
上方的 Sites
class,然后更改 Sites
class 像这样:
class PagedFilteredTableView(SingleTableView):
filter_class = None
formhelper_class = None
context_filter_name = 'filter'
paginator_class = LazyPaginator
def get_queryset(self, **kwargs):
qs = super(PagedFilteredTableView, self).get_queryset()
self.filter = self.filter_class(self.request.GET, queryset=qs)
self.filter.form.helper = self.formhelper_class()
return self.filter.qs
def get_context_data(self, **kwargs):
context = super(PagedFilteredTableView, self).get_context_data()
context[self.context_filter_name] = self.filter
return context
class Sites(LoginRequiredMixin, ExportMixin, PagedFilteredTableView):
model = Site
table_class = SiteTable
template_name = "app_settings/table_view.html"
login_url = '/login/'
redirect_field_name = 'redirect_to'
filter_class = SiteFilterEx
exclude_columns = ("buttons", )
table_pagination = {'per_page': 20}
def dispatch(self, *args, **kwargs):
site_type = get_object_or_404(SiteType, pk=self.kwargs['site_type'])
site_state = 'Open' if self.kwargs['state'] else 'Closed'
self.site_type_name = '{} {}s'.format(site_state, site_type.site_type)
self.site_type_icon = 'fa {}'.format(site_type.icon)
return super(Sites, self).dispatch(*args, **kwargs)
def get_queryset(self):
site_type = self.kwargs['site_type']
subnet = Subquery(
DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
active_link=True, \
circuit__decommissioned=False
).values('circuit__name')[:1])
active_circuit = Subquery(
DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
active_link=True, \
circuit__decommissioned=False
).values('circuit__name')[:1])
if site_type:
return super(Sites, self).get_queryset().filter(
is_live=self.kwargs['state'], site_type_id=site_type
).annotate(
active_circuit=active_circuit
).prefetch_related('sitesupernet_set')
else:
return super(Sites, self).get_queryset().filter(
is_live=self.kwargs['state']
).annotate(
active_circuit=active_circuit
).prefetch_related('sitesupernet_set')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['page_icon']=self.site_type_icon
context['page_title']=self.site_type_name
return context
下午好,
我在 django-tables2 中使用自定义的 django-filter 来通过单个输入搜索所有字段。我刚刚注意到,当我搜索时,我丢失了分页菜单。
这是link到过滤代码https://spapas.github.io/2016/09/12/django-split-query/
这是我的过滤器
class SiteFilterEx(django_filters.FilterSet):
ex = django_filters.CharFilter(label='Ex filter', method='filter_ex')
search_fields = ['location', 'bgp_as', 'opening_date','town','postcode']
def filter_ex(self, qs, name, value):
if value:
q_parts = value.split()
# Use a global q_totals
q_totals = Q()
# This part will get us all possible segmantiation of the query parts and put it in the possibilities list
combinatorics = itertools.product([True, False], repeat=len(q_parts) - 1)
possibilities = []
for combination in combinatorics:
i = 0
one_such_combination = [q_parts[i]]
for slab in combination:
i += 1
if not slab: # there is a join
one_such_combination[-1] += ' ' + q_parts[i]
else:
one_such_combination += [q_parts[i]]
possibilities.append(one_such_combination)
# Now, for all possiblities we'll append all the Q objects using OR
for p in possibilities:
list1=self.search_fields
list2=p
perms = [zip(x,list2) for x in itertools.permutations(list1,len(list2))]
for perm in perms:
q_part = Q()
for p in perm:
q_part = q_part & Q(**{p[0]+'__icontains': p[1]})
q_totals = q_totals | q_part
qs = qs.filter(q_totals)
return qs
class Meta:
model = Site
fields = ['ex']
form = SiteFilterForm
在我的模板中我可以使用:
Showing {{ filter.qs.count }} of {{ filter.queryset.count }} records
但默认的 Django-tables 使用
Showing {{ table.page.start_index }} to {{ table.page.end_index }} of {{ table.page.paginator.count }} records
当我过滤时,分页完全消失了。
我想我需要 return 一个同名的分页怎么办?但不确定我需要做什么?
这是来自视图的代码:
class Sites(LoginRequiredMixin, ExportMixin, SingleTableMixin, FilterView):
model = Site
table_class = SiteTable
template_name = "app_settings/table_view.html"
login_url = '/login/'
redirect_field_name = 'redirect_to'
filterset_class = SiteFilterEx
exclude_columns = ("buttons", )
def dispatch(self, *args, **kwargs):
site_type = get_object_or_404(SiteType, pk=self.kwargs['site_type'])
site_state = 'Open' if self.kwargs['state'] else 'Closed'
self.site_type_name = '{} {}s'.format(site_state, site_type.site_type)
self.site_type_icon = 'fa {}'.format(site_type.icon)
return super(Sites, self).dispatch(*args, **kwargs)
def get_queryset(self):
site_type = self.kwargs['site_type']
subnet = Subquery(
DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
active_link=True, \
circuit__decommissioned=False
).values('circuit__name')[:1])
active_circuit = Subquery(
DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
active_link=True, \
circuit__decommissioned=False
).values('circuit__name')[:1])
if site_type:
return super(Sites, self).get_queryset().filter(
is_live=self.kwargs['state'], site_type_id=site_type
).annotate(
active_circuit=active_circuit
).prefetch_related('sitesupernet_set')
else:
return super(Sites, self).get_queryset().filter(
is_live=self.kwargs['state']
).annotate(
active_circuit=active_circuit
).prefetch_related('sitesupernet_set')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['page_icon']=self.site_type_icon
context['page_title']=self.site_type_name
return context
以下内容可能会有所帮助: https://kuttler.eu/en/post/using-django-tables2-filters-crispy-forms-together/
特别是以下几行:
As SingleTableView inherits from Django's ListView you automatically get a Paginator object in the template context as paginator.
The get_queryset() method was changed to apply the filter and to return the filtered queryset. That filtered data ends up in the table in get_table() and gets paged. Aftert that it is added to the template context together with the filter.
我在我的项目中使用它来显示过滤的、分页的 table,使用以下内容作为我的视图的基础 class: https://gist.github.com/craigderington/0a7ded018b8401bc42822b2eeaaff1cd
像这样:
class PagedFilteredTableView(SingleTableView):
filter_class = None
formhelper_class = None
context_filter_name = 'filter'
paginator_class = LazyPaginator
def get_queryset(self, **kwargs):
qs = super(PagedFilteredTableView, self).get_queryset()
self.filter = self.filter_class(self.request.GET, queryset=qs)
self.filter.form.helper = self.formhelper_class()
return self.filter.qs
def get_context_data(self, **kwargs):
context = super(PagedFilteredTableView, self).get_context_data()
context[self.context_filter_name] = self.filter
return context
我认为class:
class JobList(LoginRequiredMixin, PermissionRequiredMixin, ExportMixin, PagedFilteredTableView):
permission_required = 'jobs.view_job'
template_name = 'jobs/job_list.html'
model = Job
context_object_name = 'job_list'
table_class = JobTable
table_pagination = {'per_page': 20}
filter_class = JobListFilter
formhelper_class = forms.JobListFilterFormHelper
# Columns to exclude in the export
exclude_columns = ('duration', 'job_type', 'contact_name', 'job_status',)
def get_queryset(self):
qs = super(JobList, self).get_queryset()
...
self.filter.form.helper.add_input(
Submit('submit', 'Filter',
css_class='btn pmd-ripple-effect btn-lg btn-primary btn-block',
style='margin-top:10px;')
)
return qs
def get_context_data(self, **kwargs):
context = super(JobList, self).get_context_data(**kwargs)
search_query = self.get_queryset()
table = JobTable(search_query)
RequestConfig(self.request).configure(table)
context['table'] = table
return context
因此,根据您的观点,您可以将 PagedFilteredTableView
class 添加到 views.py
上方的 Sites
class,然后更改 Sites
class 像这样:
class PagedFilteredTableView(SingleTableView):
filter_class = None
formhelper_class = None
context_filter_name = 'filter'
paginator_class = LazyPaginator
def get_queryset(self, **kwargs):
qs = super(PagedFilteredTableView, self).get_queryset()
self.filter = self.filter_class(self.request.GET, queryset=qs)
self.filter.form.helper = self.formhelper_class()
return self.filter.qs
def get_context_data(self, **kwargs):
context = super(PagedFilteredTableView, self).get_context_data()
context[self.context_filter_name] = self.filter
return context
class Sites(LoginRequiredMixin, ExportMixin, PagedFilteredTableView):
model = Site
table_class = SiteTable
template_name = "app_settings/table_view.html"
login_url = '/login/'
redirect_field_name = 'redirect_to'
filter_class = SiteFilterEx
exclude_columns = ("buttons", )
table_pagination = {'per_page': 20}
def dispatch(self, *args, **kwargs):
site_type = get_object_or_404(SiteType, pk=self.kwargs['site_type'])
site_state = 'Open' if self.kwargs['state'] else 'Closed'
self.site_type_name = '{} {}s'.format(site_state, site_type.site_type)
self.site_type_icon = 'fa {}'.format(site_type.icon)
return super(Sites, self).dispatch(*args, **kwargs)
def get_queryset(self):
site_type = self.kwargs['site_type']
subnet = Subquery(
DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
active_link=True, \
circuit__decommissioned=False
).values('circuit__name')[:1])
active_circuit = Subquery(
DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
active_link=True, \
circuit__decommissioned=False
).values('circuit__name')[:1])
if site_type:
return super(Sites, self).get_queryset().filter(
is_live=self.kwargs['state'], site_type_id=site_type
).annotate(
active_circuit=active_circuit
).prefetch_related('sitesupernet_set')
else:
return super(Sites, self).get_queryset().filter(
is_live=self.kwargs['state']
).annotate(
active_circuit=active_circuit
).prefetch_related('sitesupernet_set')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['page_icon']=self.site_type_icon
context['page_title']=self.site_type_name
return context