fixed up a lot of shitty documentation, and fixed some bugs while I was at it.
authorBradley Ayers <bradley.ayers@gmail.com>
Thu, 24 Feb 2011 16:54:02 +0000 (02:54 +1000)
committerBradley Ayers <bradley.ayers@gmail.com>
Thu, 24 Feb 2011 16:54:02 +0000 (02:54 +1000)
django_tables/__init__.py
django_tables/columns.py
django_tables/rows.py
django_tables/tables.py
docs/index.rst

index 7e93bcca71e6a61d64c97e76989b8629ce90e7ff..0624020c199df35d15f5458f13c2095a80404376 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf8 -*-
 # (major, minor, bugfix, "pre-alpha" | "alpha" | "beta" | "final", release | 0)
-VERSION = (0, 2, 0, 'alpha', 0)
+VERSION = (0, 2, 0, 'alpha', 1)
 
 
 def get_version():
index e721b0b4ca3438775127cb838bf32118d3f1000c..d3582621e7a059aa53ac13bd9de1e07c57addb55 100644 (file)
@@ -7,27 +7,99 @@ from django.utils.text import capfirst
 class Column(object):
     """Represents a single column of a table.
 
-    ``verbose_name`` defines a display name for this column used for output.
+    :class:`Column` objects control the way a column (including the cells that
+    fall within it) are rendered.
 
-    You can use ``visible`` to flag the column as hidden by default.
-    However, this can be overridden by the ``visibility`` argument to the
-    table constructor. If you want to make the column completely unavailable
-    to the user, set ``inaccessible`` to True.
-
-    Setting ``sortable`` to False will result in this column being unusable
-    in ordering. You can further change the *default* sort direction to
-    descending using ``direction``. Note that this option changes the actual
-    direction only indirectly. Normal und reverse order, the terms
-    django-tables exposes, now simply mean different things.
-
-    Data can be formatted by using ``formatter``, which accepts a callable as
-    an argument (e.g. lambda x: x.upper())
     """
-    # Tracks each time a Column instance is created. Used to retain order.
+    #: Tracks each time a Column instance is created. Used to retain order.
     creation_counter = 0
 
     def __init__(self, verbose_name=None, accessor=None, default=None,
                  visible=True, sortable=None, formatter=None):
+        """Initialise a :class:`Column` object.
+
+        :param verbose_name:
+            A pretty human readable version of the column name. Typically this
+            is used in the header cells in the HTML output.
+
+        :param accessor:
+            A string or callable that specifies the attribute to access when
+            retrieving the value for a cell in this column from the data-set.
+            Multiple lookups can be achieved by providing a dot separated list
+            of lookups, e.g. ``"user.first_name"``. The functionality is
+            identical to that of Django's template variable syntax, e.g. ``{{
+            user.first_name }}``
+
+            A callable should be used if the dot separated syntax is not
+            capable of describing the lookup properly. The callable will be
+            passed a single item from the data (if the table is using
+            :class:`QuerySet` data, this would be a :class:`Model` instance),
+            and is expected to return the correct value for the column.
+
+            Consider the following:
+
+            .. code-block:: python
+
+                >>> import django_tables as tables
+                >>> data = [
+                ...     {'dot.separated.key': 1},
+                ...     {'dot.separated.key': 2},
+                ... ]
+                ...
+                >>> class SlightlyComplexTable(tables.Table):
+                >>>     dot_seperated_key = tables.Column(accessor=lambda x: x['dot.separated.key'])
+                ...
+                >>> table = SlightlyComplexTable(data)
+                >>> for row in table.rows:
+                >>>     print row['dot_seperated_key']
+                ...
+                1
+                2
+
+            This would **not** have worked:
+
+            .. code-block:: python
+
+                dot_seperated_key = tables.Column(accessor='dot.separated.key')
+
+        :param default:
+            The default value for the column. This can be a value or a callable
+            object [1]_. If an object in the data provides :const:`None` for a
+            column, the default will be used instead.
+
+            The default value may affect ordering, depending on the type of
+            data the table is using. The only case where ordering is not
+            affected ing when a :class:`QuerySet` is used as the table data
+            (since sorting is performed by the database).
+
+            .. [1] The provided callable object must not expect to receive any
+               arguments.
+
+        :param visible:
+            If :const:`False`, this column will not be in HTML from output
+            generators (e.g. :meth:`as_html` or ``{% render_table %}``).
+
+            When a field is not visible, it is removed from the table's
+            :attr:`~Column.columns` iterable.
+
+        :param sortable:
+            If :const:`False`, this column will not be allowed to be used in
+            ordering the table.
+
+        :param formatter:
+            A callable object that is used as a final step in formatting the
+            value for a cell. The callable will be passed the string that would
+            have otherwise been displayed in the cell.
+
+            In the following table, cells in the *name* column have upper-case
+            values.
+
+            .. code-block:: python
+
+                class Example(tables.Table):
+                    name = tables.Column(formatter=lambda x: x.upper())
+
+        """
         if not (accessor is None or isinstance(accessor, basestring) or
                 callable(accessor)):
             raise TypeError('accessor must be a string or callable, not %s' %
@@ -47,26 +119,32 @@ class Column(object):
 
     @property
     def default(self):
-        """Since ``Column.default`` property may be a callable, this function
-        handles access.
+        """The default value for cells in this column.
+
+        The default value passed into ``Column.default`` property may be a
+        callable, this function handles access.
+
         """
         return self._default() if callable(self._default) else self._default
 
     def render(self, table, bound_column, bound_row):
         """Returns a cell's content.
         This method can be overridden by ``render_FOO`` methods on the table or
-        by subclassing ``Column``.
+        by subclassing :class:`Column`.
+        
         """
         return table.data.data_for_cell(bound_column=bound_column,
                                         bound_row=bound_row)
 
 
 class CheckBoxColumn(Column):
-    """A subclass of Column that renders its column data as a checkbox
-
-    ``name`` is the html name of the checkbox.
-    """
+    """A subclass of Column that renders its column data as a checkbox"""
     def __init__(self, attrs=None, *args, **kwargs):
+        """
+        :param attrs: a dict of HTML element attributes to be added to the
+            ``<input>``
+
+        """
         super(CheckBoxColumn, self).__init__(*args, **kwargs)
         self.attrs = attrs or {}
 
index 1883baa2be4a4025cedc41837349a4f1daa233f5..9544bd31f73e0675195305244dfb61fea54ceacc 100644 (file)
@@ -85,7 +85,9 @@ class BoundRow(object):
         custom = getattr(self.table, 'render_%s' % name, None)
         if custom:
             return custom(bound_column, self)
-        return bound_column.column.render(self.table, bound_column, self)
+        return bound_column.column.render(table=self.table,
+                                          bound_column=bound_column,
+                                          bound_row=self)
 
     def __contains__(self, item):
         """Check by both row object and column name."""
index aabc1a71c2f60a10b018535b05215c8692f947b9..df26d8f747b623807f6396561b4d229c70df4c7a 100644 (file)
@@ -5,6 +5,7 @@ from django.utils.datastructures import SortedDict
 from django.http import Http404
 from django.template.loader import get_template
 from django.template import Context
+from django.utils.encoding import StrAndUnicode
 from .utils import rmprefix, toggleprefix, OrderByTuple, Accessor
 from .columns import Column
 from .rows import Rows, BoundRow
@@ -174,7 +175,7 @@ class TableOptions(object):
         self.order_by = getattr(options, 'order_by', ())
 
 
-class Table(object):
+class Table(StrAndUnicode):
     """A collection of columns, plus their associated data rows."""
     __metaclass__ = DeclarativeColumnsMetaclass
 
index 735f57f977a16b837ac9ec767597feadfd309bba..40cdf4a9b69a467749a961185d69c4af6807c99f 100644 (file)
@@ -166,35 +166,60 @@ run-time information of the table into the formatter. For example it would not
 be possible to incorporate the row number into the cell's value.
 
 
-Column render method
---------------------
+:meth:`Table.render_FOO` Method
+-------------------------------
 
 This approach provides a lot of control, but is only suitable if you intend to
 customise the rendering for a single table (otherwise you'll end up having to
 copy & paste the method to every table you want to modify – which violates
 DRY).
 
+The example below has a number of different techniques in use:
+
+* :meth:`Column.render` (accessible via :attr:`BoundColumn.column`) applies the
+  *formatter* function if it's been provided. This is evident in the order that
+  the square and angled brackets have been applied for the ``id`` column.
+* Completely abitrary values can be returned by :meth:`render_FOO` methods, as
+  shown in :meth:`~SimpleTable.render_row_number` (a :attr:`_counter` attribute
+  is added to the :class:`SimpleTable` object to keep track of the row number).
+
+  This is possible because :meth:`render_FOO` methods override the default
+  behaviour of retrieving a value from the data-source.
+
+.. code-block:: python
+
     >>> import django_tables as tables
     >>> class SimpleTable(tables.Table):
     ...     row_number = tables.Column()
-    ...     id = tables.Column(formatter=lambda x: '#%d' % x)
+    ...     id = tables.Column(formatter=lambda x: '[%s]' % x)
     ...     age = tables.Column(formatter=lambda x: '%d years old' % x)
     ...
     ...     def render_row_number(self, bound_column, bound_row):
-    ...         value =
+    ...         value = getattr(self, '_counter', 0)
+    ...         self._counter = value + 1
+    ...         return 'Row %d' % value
     ...
     ...     def render_id(self, bound_column, bound_row):
-    ...         value = self.column.
+    ...         value = bound_column.column.render(table=self,
+    ...                                            bound_column=bound_column,
+    ...                                            bound_row=bound_row)
+    ...         return '<%s>' % value
     ...
     >>> table = SimpleTable([{'age': 31, 'id': 10}, {'age': 34, 'id': 11}])
     >>> for cell in table.rows[0]:
     ...     print cell
     ...
-    #10
+    Row 0
+    <[10]>
     31 years old
 
-If you want full control over the way the table is rendered, create
-and render the template yourself:
+
+Custom Template
+---------------
+
+And of course if you want full control over the way the table is rendered,
+ignore the built-in generation tools, and instead pass an instance of your
+:class:`Table` subclass into your own template, and render it yourself:
 
 .. code-block:: django
 
@@ -219,119 +244,65 @@ and render the template yourself:
     </table>
 
 
+Subclassing :class:`Column`
+---------------------------
 
-:class:`Columns` Objects
-========================
-
-.. autoclass:: django_tables.columns.Columns
-    :members: __init__, all, items, names, sortable, visible, __iter__,
-              __contains__, __len__, __getitem__
-
-
-:class:`BoundColumn` Objects
-============================
-
-.. autoclass:: django_tables.columns.BoundColumn
-    :members: __init__, table, column, name, accessor, default, formatter,
-              sortable, verbose_name, visible
-
-
-Column options
---------------
-
-Each column takes a certain set of column-specific arguments.
-
-There's also a set of common arguments available to all column types. All are
-optional. Here's a summary of them.
-
-    :attr:`~Column.verbose_name`
-        A pretty human readable version of the column name. Typically this is
-        used in the header cells in the HTML output.
-
-    :attr:`~Column.accessor`
-        A string or callable that specifies the attribute to access when
-        retrieving the value for a cell in this column from the data-set.
-        Multiple lookups can be achieved by providing a dot separated list of
-        lookups, e.g. ``"user.first_name"``. The functionality is identical to
-        that of Django's template variable syntax, e.g. ``{{ user.first_name
-        }}``
-
-        A callable should be used if the dot separated syntax is not capable of
-        describing the lookup properly. The callable will be passed a single
-        item from the data (if the table is using :class:`QuerySet` data, this
-        would be a :class:`Model` instance), and is expected to return the
-        correct value for the column.
-
-        Consider the following:
-
-        .. code-block:: python
-
-            >>> import django_tables as tables
-            >>> data = [
-            ...     {'dot.separated.key': 1},
-            ...     {'dot.separated.key': 2},
-            ... ]
-            ...
-            >>> class SlightlyComplexTable(tables.Table):
-            >>>     dot_seperated_key = tables.Column(accessor=lambda x: x['dot.separated.key'])
-            ...
-            >>> table = SlightlyComplexTable(data)
-            >>> for row in table.rows:
-            >>>     print row['dot_seperated_key']
-            ...
-            1
-            2
+If you want to have a column behave the same way in many tables, it's best to
+create a subclass of :class:`Column` and use that when defining the table.
 
-        This would not have worked:
+To change the way cells are rendered, simply override the
+:meth:`~Column.render` method.
 
-        .. code-block:: python
-
-            dot_seperated_key = tables.Column(accessor='dot.separated.key')
-
-    :attr:`~Column.default`
-        The default value for the column. This can be a value or a callable
-        object [1]_. If an object in the data provides :const:`None` for a
-        column, the default will be used instead.
-
-        The default value may affect ordering, depending on the type of
-        data the table is using. The only case where ordering is not affected
-        ing when a :class:`QuerySet` is used as the table data (since sorting
-        is performed by the database).
-
-        .. [1] The provided callable object must not expect to receive any
-           arguments.
-
-    :attr:`~Column.visible`
-        If :const:`False`, this column will not be in the HTML output.
-
-        When a field is not visible, it is removed from the table's
-        :attr:`~Column.columns` iterable.
-
-    :attr:`~Column.sortable`
-        If :const:`False`, this column will not be allowed to be used in
-        ordering the table.
+.. code-block:: python
 
-    :attr:`~Column.formatter`
-        A callable object that is used as a final step in formatting the value
-        for a cell. The callable will be passed the string that would have
-        otherwise been displayed in the cell.
+    >>> import django_tables as tables
+    >>>
+    >>> class AngryColumn(tables.Column):
+    ...     def render(self, *args, **kwargs):
+    ...         raw = super(AngryColumn, self).render(*args, **kwargs)
+    ...         return raw.upper()
+    ...
+    >>> class Example(tables.Table):
+    ...     normal = tables.Column()
+    ...     angry = AngryColumn()
+    ...
+    >>> data = [{
+    ...     'normal': 'May I have some food?',
+    ...     'angry': 'Give me the food now!',
+    ... }, {
+    ...     'normal': 'Hello!',
+    ...     'angry': 'What are you looking at?',
+    ... }]
+    ...
+    >>> table = Example(data)
+    >>> table.as_html()
+    u'<table><thead><tr><th>Normal</th><th>Angry</th></tr></thead><tbody><tr><td>May I have some food?</td><td>GIVE ME THE FOOD NOW!</td></tr><tr><td>Hello!</td><td>WHAT ARE YOU LOOKING AT?</td></tr></tbody></table>\n'
 
+Which, when displayed in a browser, would look something like this:
 
-Rows
-====
++-----------------------+--------------------------+
+| Normal                | Angry                    |
++=======================+==========================+
+| May I have some food? | GIVE ME THE FOOD NOW!    |
++-----------------------+--------------------------+
+| Hello!                | WHAT ARE YOU LOOKING AT? |
++-----------------------+--------------------------+
 
-:class:`Rows` Objects
-=====================
 
-.. autoclass:: django_tables.rows.Rows
-    :members: __init__, all, page, __iter__, __len__, count, __getitem__
+If you plan on returning HTML from a :meth:`~Column.render` method, you must
+remember to mark it as safe (otherwise it will be escaped when the table is
+rendered). This can be achieved by using the :func:`mark_safe` function.
 
+.. code-block:: python
 
-:class:`BoundRow` Objects
-=========================
+    >>> from django.utils.safestring import mark_safe
+    >>>
+    >>> class ImageColumn(tables.Column):
+    ...     def render(self, **kwargs):
+    ...         raw = super(AngryColumn, self).render(**kwargs)
+    ...         return mark_safe('<img src="/media/img/%s.jpg" />' % raw)
+    ...
 
-.. autoclass:: django_tables.rows.BoundRow
-    :members: __init__, values, __getitem__, __contains__, __iter__
 
 
 .. _template_tags:
@@ -404,3 +375,44 @@ which can be iterated over:
         {% endfor %}
         </tbody>
     </table>
+
+
+API Reference
+=============
+
+:class:`Column` Objects:
+------------------------
+
+
+.. autoclass:: django_tables.columns.Column
+    :members: __init__, default, render
+
+
+:class:`Columns` Objects
+------------------------
+
+.. autoclass:: django_tables.columns.Columns
+    :members: __init__, all, items, names, sortable, visible, __iter__,
+              __contains__, __len__, __getitem__
+
+
+:class:`BoundColumn` Objects
+----------------------------
+
+.. autoclass:: django_tables.columns.BoundColumn
+    :members: __init__, table, column, name, accessor, default, formatter,
+              sortable, verbose_name, visible
+
+
+:class:`Rows` Objects
+---------------------
+
+.. autoclass:: django_tables.rows.Rows
+    :members: __init__, all, page, __iter__, __len__, count, __getitem__
+
+
+:class:`BoundRow` Objects
+-------------------------
+
+.. autoclass:: django_tables.rows.BoundRow
+    :members: __init__, values, __getitem__, __contains__, __iter__