Skip to content

Commit

Permalink
Added the ability to exclude columns from an entire Table class or fr…
Browse files Browse the repository at this point in the history
…om a specific instance. Resolves issue #4.
  • Loading branch information
bradleyayers committed Jun 2, 2011
1 parent 4f355b7 commit 9589d92
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 93 deletions.
8 changes: 4 additions & 4 deletions django_tables/columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,17 +434,17 @@ def visible(self):

class BoundColumns(object):
"""
Container for spawning BoundColumns.
Container for spawning :class:`.BoundColumn` objects.
This is bound to a table and provides its :attr:`.Table.columns` property.
It provides access to those columns in different ways (iterator,
item-based, filtered and unfiltered etc), stuff that would not be possible
with a simple iterator in the table class.
A :class:`.BoundColumns` object is a container for holding
:class:`.BoundColumn` objects. It provides methods that make accessing
A ``BoundColumns`` object is a container for holding
``BoundColumn`` objects. It provides methods that make accessing
columns easier than if they were stored in a ``list`` or
:class:`dict`. :class:`Columns` has a similar API to a ``dict`` (it
``dict``. ``Columns`` has a similar API to a ``dict`` (it
actually uses a ``SortedDict`` interally).
At the moment you'll only come across this class when you access a
Expand Down
54 changes: 28 additions & 26 deletions django_tables/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class TableData(object):
Exposes a consistent API for :term:`table data`. It currently supports a
:class:`QuerySet`, or a :class:`list` of :class:`dict` objects.
This class is used by :class:.Table` to wrap any
This class is used by :class:`.Table` to wrap any
input table data.
"""

Expand Down Expand Up @@ -92,7 +92,7 @@ class DeclarativeColumnsMetaclass(type):
as well.
"""

def __new__(cls, name, bases, attrs, parent_cols_from=None):
def __new__(cls, name, bases, attrs):
"""Ughhh document this :)"""
# extract declared columns
columns = [(name, attrs.pop(name)) for name, column in attrs.items()
Expand All @@ -104,46 +104,44 @@ def __new__(cls, name, bases, attrs, parent_cols_from=None):
# well. Note that we loop over the bases in *reverse* - this is
# necessary to preserve the correct order of columns.
for base in bases[::-1]:
cols_attr = (parent_cols_from if (parent_cols_from and
hasattr(base, parent_cols_from))
else 'base_columns')
if hasattr(base, cols_attr):
columns = getattr(base, cols_attr).items() + columns
if hasattr(base, "base_columns"):
columns = base.base_columns.items() + columns
# Note that we are reusing an existing ``base_columns`` attribute.
# This is because in certain inheritance cases (mixing normal and
# ModelTables) this metaclass might be executed twice, and we need
# to avoid overriding previous data (because we pop() from attrs,
# the second time around columns might not be registered again).
# An example would be:
# class MyNewTable(MyOldNonModelTable, tables.ModelTable): pass
if not 'base_columns' in attrs:
attrs['base_columns'] = SortedDict()
attrs['base_columns'].update(SortedDict(columns))
attrs['_meta'] = TableOptions(attrs.get('Meta', None))
# class MyNewTable(MyOldNonTable, tables.Table): pass
if not "base_columns" in attrs:
attrs["base_columns"] = SortedDict()
attrs["base_columns"].update(SortedDict(columns))
attrs["_meta"] = opts = TableOptions(attrs.get("Meta", None))
for ex in opts.exclude:
if ex in attrs["base_columns"]:
attrs["base_columns"].pop(ex)
return type.__new__(cls, name, bases, attrs)


class TableOptions(object):
"""
Extracts and exposes options for a :class:`.Table` from a ``class Meta``
when the table is defined.
:param options: options for a table
:type options: :class:`Meta` on a :class:`.Table`
"""

def __init__(self, options=None):
"""
:param options: options for a table
:type options: :class:`Meta` on a :class:`.Table`
"""
super(TableOptions, self).__init__()
self.sortable = getattr(options, 'sortable', True)
order_by = getattr(options, 'order_by', ())
self.attrs = AttributeDict(getattr(options, "attrs", {}))
self.empty_text = getattr(options, "empty_text", None)
self.exclude = getattr(options, "exclude", ())
order_by = getattr(options, "order_by", ())
if isinstance(order_by, basestring):
order_by = (order_by, )
self.order_by = OrderByTuple(order_by)
self.attrs = AttributeDict(getattr(options, 'attrs', {}))
self.empty_text = getattr(options, 'empty_text', None)
self.sortable = getattr(options, "sortable", True)


class Table(StrAndUnicode):
Expand Down Expand Up @@ -186,21 +184,25 @@ def obj_list(request):
__metaclass__ = DeclarativeColumnsMetaclass
TableDataClass = TableData

def __init__(self, data, order_by=None, sortable=None, empty_text=None):
self._rows = BoundRows(self) # bound rows
self._columns = BoundColumns(self) # bound columns
def __init__(self, data, order_by=None, sortable=None, empty_text=None,
exclude=None):
self._rows = BoundRows(self)
self._columns = BoundColumns(self)
self._data = self.TableDataClass(data=data, table=self)
self.empty_text = empty_text
self.sortable = sortable
if order_by is None:
self.order_by = self._meta.order_by
else:
self.order_by = order_by

# Make a copy so that modifying this will not touch the class
# definition. Note that this is different from forms, where the
# copy is made available in a ``fields`` attribute.
self.base_columns = copy.deepcopy(type(self).base_columns)
self.exclude = exclude or ()
for ex in self.exclude:
if ex in self.base_columns:
self.base_columns.pop(ex)

def __unicode__(self):
return self.as_html()
Expand Down
67 changes: 50 additions & 17 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -636,16 +636,64 @@ API Reference

.. class:: Table.Meta

Provides a way to define *global* settings for table, as opposed to
defining them for each instance.

.. attribute:: attrs

Allows custom HTML attributes to be specified which will be added to
the ``<table>`` tag of any table rendered via
:meth:`~django_tables.tables.Table.as_html` or the
:ref:`template-tags.render_table` template tag.

This is typically used to enable a theme for a table (which is done by
adding a CSS class to the ``<table>`` element). i.e.::

class SimpleTable(tables.Table):
name = tables.Column()

class Meta:
attrs = {"class": "paleblue"}

:type: ``dict``

Default: ``{}``

:type: :class:`dict`
.. attribute:: empty_text

Defines the text to display when the table has no rows.

.. attribute:: exclude

Defines which columns should be excluded from the table. This is useful
in subclasses to exclude columns in a parent. e.g.

>>> class Person(tables.Table):
... first_name = tables.Column()
... last_name = tables.Column()
...
>>> Person.base_columns
{'first_name': <django_tables.columns.Column object at 0x10046df10>,
'last_name': <django_tables.columns.Column object at 0x10046d8d0>}
>>> class ForgetfulPerson(Person):
... class Meta:
... exclude = ("last_name", )
...
>>> ForgetfulPerson.base_columns
{'first_name': <django_tables.columns.Column object at 0x10046df10>}

:type: tuple of ``string`` objects

Default: ``()``

.. attribute:: order_by

The default ordering. e.g. ``('name', '-age')``. A hyphen ``-`` can be
used to prefix a column name to indicate *descending* order.

:type: :class:`tuple`

Default: ``()``

.. attribute:: sortable

Expand All @@ -656,17 +704,9 @@ API Reference
an easy mechanism to disable sorting on an entire table, without adding
``sortable=False`` to each ``Column`` in a ``Table``.

Default: :const:`True`

:type: :class:`bool`

.. attribute:: order_by

The default ordering. e.g. ``('name', '-age')``

Default: ``()``

:type: :class:`tuple`
Default: :const:`True`


:class:`TableData` Objects:
Expand All @@ -676,13 +716,6 @@ API Reference
:members: __init__, order_by, __getitem__, __len__


:class:`TableOptions` Objects:
------------------------------

.. autoclass:: django_tables.tables.TableOptions
:members:


:class:`Column` Objects:
------------------------

Expand Down
Loading

0 comments on commit 9589d92

Please sign in to comment.