summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--horizon/tables/base.py25
-rw-r--r--horizon/tables/views.py2
-rw-r--r--horizon/templates/horizon/common/_data_table_row_actions.html2
-rw-r--r--horizon/tests/table_tests.py21
4 files changed, 37 insertions, 13 deletions
diff --git a/horizon/tables/base.py b/horizon/tables/base.py
index ea14b1234..fc39f362d 100644
--- a/horizon/tables/base.py
+++ b/horizon/tables/base.py
@@ -617,13 +617,14 @@ class DataTableMetaclass(type):
# Gather columns; this prevents the column from being an attribute
# on the DataTable class and avoids naming conflicts.
- columns = [(column_name, attrs.pop(column_name)) for
- column_name, obj in attrs.items()
- if issubclass(type(obj), (opts.column_class, Column))]
- # add a name attribute to each column
- for column_name, column in columns:
- column.name = column_name
+ columns = []
+ for name, obj in attrs.items():
+ if issubclass(type(obj), (opts.column_class, Column)):
+ column_instance = attrs.pop(name)
+ column_instance.name = name
+ columns.append((name, column_instance))
columns.sort(key=lambda x: x[1].creation_counter)
+
# Iterate in reverse to preserve final order
for base in bases[::-1]:
if hasattr(base, 'base_columns'):
@@ -651,7 +652,8 @@ class DataTableMetaclass(type):
actions_column.classes.append('actions_column')
actions_column.auto = "actions"
columns.append(("actions", actions_column))
- attrs['columns'] = SortedDict(columns)
+ # Store this set of columns internally so we can copy them per-instance
+ attrs['_columns'] = SortedDict(columns)
# Gather and register actions for later access since we only want
# to instantiate them once.
@@ -698,11 +700,16 @@ class DataTable(object):
def __init__(self, request, data=None, **kwargs):
self._meta.request = request
self._meta.data = data
- self._populate_data_cache()
self.kwargs = kwargs
- for column in self.columns.values():
+ # Create a new set
+ columns = []
+ for key, _column in self._columns.items():
+ column = copy.copy(_column)
column.table = self
+ columns.append((key, column))
+ self.columns = SortedDict(columns)
+ self._populate_data_cache()
# Associate these actions with this table
for action in self.base_actions.values():
diff --git a/horizon/tables/views.py b/horizon/tables/views.py
index 810118105..612afcac2 100644
--- a/horizon/tables/views.py
+++ b/horizon/tables/views.py
@@ -39,7 +39,7 @@ class MultiTableMixin(object):
def get_tables(self):
if not self.table_classes:
- raise AttributeError('You must specify a one or more DataTable '
+ raise AttributeError('You must specify one or more DataTable '
'classes for the "table_classes" attribute '
'on %s.' % self.__class__.__name__)
if not self._tables:
diff --git a/horizon/templates/horizon/common/_data_table_row_actions.html b/horizon/templates/horizon/common/_data_table_row_actions.html
index fe223d03f..3193b69dc 100644
--- a/horizon/templates/horizon/common/_data_table_row_actions.html
+++ b/horizon/templates/horizon/common/_data_table_row_actions.html
@@ -1,5 +1,6 @@
{% load horizon %}
+{% spaceless %} {# This makes sure whitespace doesn't affect positioning for dropdown. #}
{% if row_actions|length > 1 %}
<div class="btn-group">
{% for action in row_actions %}
@@ -25,3 +26,4 @@
{% include "horizon/common/_data_table_row_action.html" %}
{% endfor %}
{% endif %}
+{% endspaceless %}
diff --git a/horizon/tests/table_tests.py b/horizon/tests/table_tests.py
index ce5f843ba..fb50df0b1 100644
--- a/horizon/tests/table_tests.py
+++ b/horizon/tests/table_tests.py
@@ -308,9 +308,9 @@ class DataTableTests(test.TestCase):
self.table = MyTable(self.request, TEST_DATA)
row = self.table.get_rows()[0]
row3 = self.table.get_rows()[2]
- id_col = self.table.base_columns['id']
- name_col = self.table.base_columns['name']
- value_col = self.table.base_columns['value']
+ id_col = self.table.columns['id']
+ name_col = self.table.columns['name']
+ value_col = self.table.columns['value']
# transform
self.assertEqual(row.cells['id'].data, '1') # Standard attr access
self.assertEqual(row.cells['name'].data, 'custom object_1') # Callable
@@ -552,3 +552,18 @@ class DataTableTests(test.TestCase):
row_actions = self.table.get_row_actions(TEST_DATA[0])
self.assertEqual(unicode(row_actions[0].verbose_name), "Delete Me")
self.assertEqual(unicode(row_actions[1].verbose_name), "Log In")
+
+ def test_column_uniqueness(self):
+ table1 = MyTable(self.request)
+ table2 = MyTable(self.request)
+ # Regression test for launchpad bug 964345.
+ self.assertNotEqual(id(table1), id(table2))
+ self.assertNotEqual(id(table1.columns), id(table2.columns))
+ t1cols = table1.columns.values()
+ t2cols = table2.columns.values()
+ self.assertEqual(t1cols[0].name, t2cols[0].name)
+ self.assertNotEqual(id(t1cols[0]), id(t2cols[0]))
+ self.assertNotEqual(id(t1cols[0].table),
+ id(t2cols[0].table))
+ self.assertNotEqual(id(t1cols[0].table._data_cache),
+ id(t2cols[0].table._data_cache))