Skip to content

Commit

Permalink
Ensure DataTableView applies filter after redirect
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Tehsmash committed Oct 15, 2014
1 parent 05d0f07 commit 9ca494e
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 38 deletions.
9 changes: 6 additions & 3 deletions horizon/tables/base.py
Expand Up @@ -1178,17 +1178,20 @@ def filtered_data(self):
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
Expand Down
9 changes: 4 additions & 5 deletions horizon/tables/views.py
Expand Up @@ -226,11 +226,10 @@ def get_server_filter_info(self, request):
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)
Expand Down
62 changes: 51 additions & 11 deletions horizon/test/tests/tables.py
Expand Up @@ -1343,25 +1343,65 @@ def test_multi_table_view_authorized(self):
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):
Expand Down
12 changes: 1 addition & 11 deletions openstack_dashboard/dashboards/admin/instances/tables.py
Expand Up @@ -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),
Expand All @@ -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 = (
Expand Down
26 changes: 18 additions & 8 deletions openstack_dashboard/dashboards/admin/instances/views.py
Expand Up @@ -73,6 +73,24 @@ def get_data(self):
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,
Expand All @@ -99,14 +117,6 @@ def get_data(self):
# 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.
Expand Down

0 comments on commit 9ca494e

Please sign in to comment.