Ensure DataTableView applies filter after redirect

Non-API bound filters were not persisting across pages changes,
this patch adds an additional check to make sure the DataTable looks
for any non api bound filters in the session and applies them on page load.
Also added some logic to make Project filter in Admin->Instances API
bound using the tenant_id.

Change-Id: Ieab9f2b92b59401809725a4f37628757dc4c8f13
Closes-Bug: 1369014
This commit is contained in:
Sam Betts 2014-09-15 13:35:17 +01:00
parent 05d0f07317
commit 9ca494e6e3
5 changed files with 80 additions and 38 deletions

View File

@ -1178,17 +1178,20 @@ class DataTable(object):
if self._meta.filter and self._meta._filter_action:
action = self._meta._filter_action
filter_string = self.get_filter_string()
filter_field = self.get_filter_field()
request_method = self.request.method
needs_preloading = (not filter_string
and request_method == 'GET'
and action.needs_preloading)
valid_method = (request_method == action.method)
if valid_method or needs_preloading:
filter_field = self.get_filter_field()
not_api_filter = (filter_string
and not action.is_api_filter(filter_field))
if valid_method or needs_preloading or not_api_filter:
if self._meta.mixed_data_type:
self._filtered_data = action.data_type_filter(
self, self.data, filter_string)
elif not action.is_api_filter(filter_field):
else:
self._filtered_data = action.filter(
self, self.data, filter_string)
return self._filtered_data

View File

@ -226,11 +226,10 @@ class DataTableView(MultiTableView):
return None
param_name = filter_action.get_param_name()
filter_string = request.POST.get(param_name)
filter_string_session = request.session.get(param_name)
changed = (filter_string is not None and
filter_string_session is not None and
filter_string != filter_string_session)
if filter_string is None and filter_string_session is not None:
filter_string_session = request.session.get(param_name, "")
changed = (filter_string is not None
and filter_string != filter_string_session)
if filter_string is None:
filter_string = filter_string_session
filter_field_param = param_name + '_field'
filter_field = request.POST.get(filter_field_param)

View File

@ -1343,25 +1343,65 @@ class DataTableViewTests(test.TestCase):
self.assertEqual(TableWithPermissions,
context['table_with_permissions_table'].__class__)
def test_api_filter_table_view(self):
filter_value_param = "my_table__filter__q"
filter_field_param = '%s_field' % filter_value_param
req = self.factory.post('/my_url/', {filter_value_param: 'up',
filter_field_param: 'status'})
req.user = self.user
fil_value_param = "my_table__filter__q"
fil_field_param = '%s_field' % fil_value_param
def _test_filter_setup_view(self, request):
view = APIFilterTableView()
view.request = req
view.request = request
view.kwargs = {}
view.handle_server_filter(req)
view.handle_server_filter(request)
return view
def test_api_filter_table_view(self):
req = self.factory.post('/my_url/', {self.fil_value_param: 'up',
self.fil_field_param: 'status'})
req.user = self.user
view = self._test_filter_setup_view(req)
data = view.get_data()
context = view.get_context_data()
self.assertEqual(context['table'].__class__, MyServerFilterTable)
data = view.get_data()
self.assertQuerysetEqual(data,
['<FakeObject: object_1>',
'<FakeObject: object_2>',
'<FakeObject: object_3>'])
self.assertEqual(req.session.get(filter_value_param), 'up')
self.assertEqual(req.session.get(filter_field_param), 'status')
self.assertEqual(req.session.get(self.fil_value_param), 'up')
self.assertEqual(req.session.get(self.fil_field_param), 'status')
def test_filter_changed_deleted(self):
req = self.factory.post('/my_url/', {self.fil_value_param: '',
self.fil_field_param: 'status'})
req.session[self.fil_value_param] = 'up'
req.session[self.fil_field_param] = 'status'
req.user = self.user
view = self._test_filter_setup_view(req)
context = view.get_context_data()
self.assertEqual(context['table'].__class__, MyServerFilterTable)
self.assertEqual(req.session.get(self.fil_value_param), '')
self.assertEqual(req.session.get(self.fil_field_param), 'status')
def test_filter_changed_nothing_sent(self):
req = self.factory.post('/my_url/', {})
req.session[self.fil_value_param] = 'up'
req.session[self.fil_field_param] = 'status'
req.user = self.user
view = self._test_filter_setup_view(req)
context = view.get_context_data()
self.assertEqual(context['table'].__class__, MyServerFilterTable)
self.assertEqual(req.session.get(self.fil_value_param), 'up')
self.assertEqual(req.session.get(self.fil_field_param), 'status')
def test_filter_changed_new_filter_sent(self):
req = self.factory.post('/my_url/', {self.fil_value_param: 'down',
self.fil_field_param: 'status'})
req.session[self.fil_value_param] = 'up'
req.session[self.fil_field_param] = 'status'
req.user = self.user
view = self._test_filter_setup_view(req)
context = view.get_context_data()
self.assertEqual(context['table'].__class__, MyServerFilterTable)
self.assertEqual(req.session.get(self.fil_value_param), 'down')
self.assertEqual(req.session.get(self.fil_field_param), 'status')
class FormsetTableTests(test.TestCase):

View File

@ -90,7 +90,7 @@ class AdminInstanceFilterAction(tables.FilterAction):
# session property used for persisting the filter.
name = "filter_admin_instances"
filter_type = "server"
filter_choices = (('project', _("Project"), False),
filter_choices = (('project', _("Project"), True),
('host', _("Host ="), True),
('name', _("Name"), True),
('ip', _("IPv4 Address ="), True),
@ -99,16 +99,6 @@ class AdminInstanceFilterAction(tables.FilterAction):
('image', _("Image ID ="), True),
('flavor', _("Flavor ID ="), True))
def filter(self, table, instances, filter_string):
"""Server side search.
When filtering is supported in the api, then we will handle in view
"""
filter_field = table.get_filter_field()
if filter_field == 'project' and filter_string:
return [inst for inst in instances
if inst.tenant_name == filter_string]
return instances
class AdminInstancesTable(tables.DataTable):
TASK_STATUS_CHOICES = (

View File

@ -73,6 +73,24 @@ class AdminIndexView(tables.DataTableView):
marker = self.request.GET.get(
project_tables.AdminInstancesTable._meta.pagination_param, None)
search_opts = self.get_filters({'marker': marker, 'paginate': True})
# Gather our tenants to correlate against IDs
try:
tenants, has_more = api.keystone.tenant_list(self.request)
except Exception:
tenants = []
msg = _('Unable to retrieve instance project information.')
exceptions.handle(self.request, msg)
if 'project' in search_opts:
ten_filter_ids = [t.id for t in tenants
if t.name == search_opts['project']]
del search_opts['project']
if len(ten_filter_ids) > 0:
search_opts['tenant_id'] = ten_filter_ids[0]
else:
self._more = False
return []
try:
instances, self._more = api.nova.server_list(
self.request,
@ -99,14 +117,6 @@ class AdminIndexView(tables.DataTableView):
# If fails to retrieve flavor list, creates an empty list.
flavors = []
# Gather our tenants to correlate against IDs
try:
tenants, has_more = api.keystone.tenant_list(self.request)
except Exception:
tenants = []
msg = _('Unable to retrieve instance project information.')
exceptions.handle(self.request, msg)
full_flavors = SortedDict([(f.id, f) for f in flavors])
tenant_dict = SortedDict([(t.id, t) for t in tenants])
# Loop through instances to get flavor and tenant info.