Copies column instances to be unique per table instance.

Fixes bug 964345.

Incidentally fixes bug 964351 as well by wrapping the dropdown
actions template code in a spaceless tag.

Change-Id: I672cb517d230db235c90a403e9b8ac0740e8732d
This commit is contained in:
Gabriel Hurley 2012-03-24 23:01:19 -07:00
parent 28defb5ca9
commit 52b9e1982b
4 changed files with 37 additions and 13 deletions

View File

@ -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():

View File

@ -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:

View File

@ -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 %}

View File

@ -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))