Merge remote-tracking branch 'jthurner/docs-fix-toc'
Conflicts: docs/index.rst
This commit is contained in:
commit
d2ed593abe
1269
docs/index.rst
1269
docs/index.rst
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
|||
.. _accessors:
|
||||
|
||||
Specifying alternative data for a column
|
||||
========================================
|
||||
|
||||
Each column has a "key" that describes which value to pull from each record to
|
||||
populate the column's cells. By default, this key is just the name given to the
|
||||
column, but it can be changed to allow foreign key traversal or other complex
|
||||
cases.
|
||||
|
||||
To reduce ambiguity, rather than calling it a "key", it's been given the
|
||||
special name "accessor".
|
||||
|
||||
Accessors are just dotted paths that describe how an object should be traversed
|
||||
to reach a specific value. To demonstrate how they work we'll use them
|
||||
directly::
|
||||
|
||||
>>> from django_tables2 import A
|
||||
>>> data = {"abc": {"one": {"two": "three"}}}
|
||||
>>> A("abc.one.two").resolve(data)
|
||||
"three"
|
||||
|
||||
Dots represent a relationships, and are attempted in this order:
|
||||
|
||||
1. Dictionary lookup ``a[b]``
|
||||
2. Attribute lookup ``a.b``
|
||||
3. List index lookup ``a[int(b)]``
|
||||
|
||||
Then, if the value is callable, it is called and the result is used.
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
API Reference
|
||||
=============
|
||||
|
||||
`.Accessor` (`.A`)
|
||||
------------------
|
||||
|
||||
.. autoclass:: django_tables2.utils.Accessor
|
||||
|
||||
|
||||
`.RequestConfig`
|
||||
----------------
|
||||
|
||||
.. autoclass:: django_tables2.config.RequestConfig
|
||||
|
||||
|
||||
`.Table`
|
||||
--------
|
||||
|
||||
.. autoclass:: django_tables2.tables.Table
|
||||
:members: paginate, as_html
|
||||
|
||||
|
||||
`.Table.Meta`
|
||||
-------------
|
||||
|
||||
.. 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:`.Table.as_html` or the
|
||||
:ref:`template-tags.render_table` template tag.
|
||||
|
||||
:type: `dict`
|
||||
:default: ``{}``
|
||||
|
||||
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"}
|
||||
|
||||
.. versionadded:: 0.15.0
|
||||
|
||||
It's possible to use callables to create *dynamic* values. A few caveats:
|
||||
|
||||
- It's not supported for ``dict`` keys, i.e. only values.
|
||||
- All values will be resolved on table instantiation.
|
||||
|
||||
Consider this example where a unique ``id`` is given to each instance
|
||||
of the table::
|
||||
|
||||
import itertools
|
||||
counter = itertools.count()
|
||||
|
||||
class UniqueIdTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
class Meta:
|
||||
attrs = {"id": lambda: "table_%d" % next(counter)}
|
||||
|
||||
.. note::
|
||||
|
||||
This functionality is also available via the ``attrs`` keyword
|
||||
argument to a table's constructor.
|
||||
|
||||
.. attribute:: empty_text
|
||||
|
||||
Defines the text to display when the table has no rows.
|
||||
|
||||
:type: `unicode`
|
||||
:default: `None`
|
||||
|
||||
If the table is empty and ``bool(empty_text)`` is `True`, a row is
|
||||
displayed containing ``empty_text``. This is allows a message such as
|
||||
*There are currently no FOO.* to be displayed.
|
||||
|
||||
.. note::
|
||||
|
||||
This functionality is also available via the ``empty_text`` keyword
|
||||
argument to a table's constructor.
|
||||
|
||||
.. attribute:: exclude
|
||||
|
||||
Defines which columns should be excluded from the table. This is useful
|
||||
in subclasses to exclude columns in a parent.
|
||||
|
||||
:type: tuple of `unicode`
|
||||
:default: ``()``
|
||||
|
||||
Example::
|
||||
|
||||
>>> class Person(tables.Table):
|
||||
... first_name = tables.Column()
|
||||
... last_name = tables.Column()
|
||||
...
|
||||
>>> Person.base_columns
|
||||
{'first_name': <django_tables2.columns.Column object at 0x10046df10>,
|
||||
'last_name': <django_tables2.columns.Column object at 0x10046d8d0>}
|
||||
>>> class ForgetfulPerson(Person):
|
||||
... class Meta:
|
||||
... exclude = ("last_name", )
|
||||
...
|
||||
>>> ForgetfulPerson.base_columns
|
||||
{'first_name': <django_tables2.columns.Column object at 0x10046df10>}
|
||||
|
||||
.. note::
|
||||
|
||||
This functionality is also available via the ``exclude`` keyword
|
||||
argument to a table's constructor.
|
||||
|
||||
However, unlike some of the other `.Table.Meta` options, providing the
|
||||
``exclude`` keyword to a table's constructor **won't override** the
|
||||
`.Meta.exclude`. Instead, it will be effectively be *added*
|
||||
to it. i.e. you can't use the constructor's ``exclude`` argument to
|
||||
*undo* an exclusion.
|
||||
|
||||
.. attribute:: fields
|
||||
|
||||
Used in conjunction with `~.Table.Meta.model`, specifies which fields
|
||||
should have columns in the table.
|
||||
|
||||
:type: tuple of `unicode` or `None`
|
||||
:default: `None`
|
||||
|
||||
If `None`, all fields are used, otherwise only those named.
|
||||
|
||||
Example::
|
||||
|
||||
# models.py
|
||||
class Person(models.Model):
|
||||
first_name = models.CharField(max_length=200)
|
||||
last_name = models.CharField(max_length=200)
|
||||
|
||||
# tables.py
|
||||
class PersonTable(tables.Table):
|
||||
class Meta:
|
||||
model = Person
|
||||
fields = ("first_name", )
|
||||
|
||||
.. attribute:: model
|
||||
|
||||
A model to inspect and automatically create corresponding columns.
|
||||
|
||||
:type: Django model
|
||||
:default: `None`
|
||||
|
||||
This option allows a Django model to be specified to cause the table to
|
||||
automatically generate columns that correspond to the fields in a
|
||||
model.
|
||||
|
||||
.. 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: `tuple`
|
||||
:default: ``()``
|
||||
|
||||
.. note::
|
||||
|
||||
This functionality is also available via the ``order_by`` keyword
|
||||
argument to a table's constructor.
|
||||
|
||||
.. attribute:: sequence
|
||||
|
||||
The sequence of the table columns. This allows the default order of
|
||||
columns (the order they were defined in the Table) to be overridden.
|
||||
|
||||
:type: any iterable (e.g. `tuple` or `list`)
|
||||
:default: ``()``
|
||||
|
||||
The special item ``"..."`` can be used as a placeholder that will be
|
||||
replaced with all the columns that weren't explicitly listed. This
|
||||
allows you to add columns to the front or back when using inheritence.
|
||||
|
||||
Example::
|
||||
|
||||
>>> class Person(tables.Table):
|
||||
... first_name = tables.Column()
|
||||
... last_name = tables.Column()
|
||||
...
|
||||
... class Meta:
|
||||
... sequence = ("last_name", "...")
|
||||
...
|
||||
>>> Person.base_columns.keys()
|
||||
['last_name', 'first_name']
|
||||
|
||||
The ``"..."`` item can be used at most once in the sequence value. If
|
||||
it's not used, every column *must* be explicitly included. e.g. in the
|
||||
above example, ``sequence = ("last_name", )`` would be **invalid**
|
||||
because neither ``"..."`` or ``"first_name"`` were included.
|
||||
|
||||
.. note::
|
||||
|
||||
This functionality is also available via the ``sequence`` keyword
|
||||
argument to a table's constructor.
|
||||
|
||||
.. attribute:: orderable
|
||||
|
||||
Default value for column's *orderable* attribute.
|
||||
|
||||
:type: `bool`
|
||||
:default: `True`
|
||||
|
||||
If the table and column don't specify a value, a column's
|
||||
``orderable`` value will fallback to this. object specify. This
|
||||
provides an easy mechanism to disable ordering on an entire table,
|
||||
without adding ``orderable=False`` to each column in a table.
|
||||
|
||||
.. note::
|
||||
|
||||
This functionality is also available via the ``orderable`` keyword
|
||||
argument to a table's constructor.
|
||||
|
||||
.. attribute:: template
|
||||
|
||||
The default template to use when rendering the table.
|
||||
|
||||
:type: `unicode`
|
||||
:default: ``"django_tables2/table.html"``
|
||||
|
||||
.. note::
|
||||
|
||||
This functionality is also available via the *template* keyword
|
||||
argument to a table's constructor.
|
||||
|
||||
|
||||
.. attribute:: localize
|
||||
|
||||
Specifies which fields should be localized in the table.
|
||||
Read :ref:`localization-control` for more information.
|
||||
|
||||
:type: tuple of `unicode`
|
||||
:default: empty tuple
|
||||
|
||||
|
||||
.. attribute:: unlocalize
|
||||
|
||||
Specifies which fields should be unlocalized in the table.
|
||||
Read :ref:`localization-control` for more information.
|
||||
|
||||
:type: tuple of `unicode`
|
||||
:default: empty tuple
|
||||
|
||||
|
||||
`.BooleanColumn`
|
||||
----------------
|
||||
|
||||
.. autoclass:: django_tables2.columns.BooleanColumn
|
||||
|
||||
|
||||
`.Column`
|
||||
---------
|
||||
|
||||
.. autoclass:: django_tables2.columns.Column
|
||||
|
||||
|
||||
`.CheckBoxColumn`
|
||||
-----------------
|
||||
|
||||
.. autoclass:: django_tables2.columns.CheckBoxColumn
|
||||
:members:
|
||||
|
||||
|
||||
`.DateColumn`
|
||||
-------------
|
||||
|
||||
.. autoclass:: django_tables2.columns.DateColumn
|
||||
:members:
|
||||
|
||||
|
||||
`.DateTimeColumn`
|
||||
-----------------
|
||||
|
||||
.. autoclass:: django_tables2.columns.DateTimeColumn
|
||||
:members:
|
||||
|
||||
|
||||
`.EmailColumn`
|
||||
--------------
|
||||
|
||||
.. autoclass:: django_tables2.columns.EmailColumn
|
||||
:members:
|
||||
|
||||
|
||||
`.FileColumn`
|
||||
-------------
|
||||
|
||||
.. autoclass:: django_tables2.columns.FileColumn
|
||||
:members:
|
||||
|
||||
|
||||
`.LinkColumn`
|
||||
-------------
|
||||
|
||||
.. autoclass:: django_tables2.columns.LinkColumn
|
||||
:members:
|
||||
|
||||
|
||||
`.TemplateColumn`
|
||||
-----------------
|
||||
|
||||
.. autoclass:: django_tables2.columns.TemplateColumn
|
||||
:members:
|
||||
|
||||
|
||||
`.URLColumn`
|
||||
------------
|
||||
|
||||
.. autoclass:: django_tables2.columns.URLColumn
|
||||
:members:
|
||||
|
||||
|
||||
See :doc:`internal` for internal classes.
|
|
@ -0,0 +1,18 @@
|
|||
.. _builtin-columns:
|
||||
|
||||
Built-in columns
|
||||
================
|
||||
|
||||
For common use-cases the following columns are included:
|
||||
|
||||
- `.BooleanColumn` -- renders boolean values
|
||||
- `.Column` -- generic column
|
||||
- `.CheckBoxColumn` -- renders checkbox form inputs
|
||||
- `.DateColumn` -- date formatting
|
||||
- `.DateTimeColumn` -- datetime formatting in the local timezone
|
||||
- `.FileColumn` -- renders files as links
|
||||
- `.EmailColumn` -- renders ``<a href="mailto:...">`` tags
|
||||
- `.LinkColumn` -- renders ``<a href="...">`` tags (compose a django url)
|
||||
- `.TemplateColumn` -- renders template code
|
||||
- `.URLColumn` -- renders ``<a href="...">`` tags (absolute url)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
.. _column-attributes:
|
||||
|
||||
Column attributes
|
||||
=================
|
||||
|
||||
Column attributes can be specified using the `dict` with specific keys.
|
||||
The dict defines HTML attributes for one of more elements within the column.
|
||||
Depending on the column, different elements are supported, however ``th``,
|
||||
``td``, and ``cell`` are supported universally.
|
||||
|
||||
e.g.
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
>>> import django_tables2 as tables
|
||||
>>>
|
||||
>>> class SimpleTable(tables.Table):
|
||||
... name = tables.Column(attrs={"th": {"id": "foo"}})
|
||||
...
|
||||
>>> SimpleTable(data).as_html()
|
||||
"{snip}<thead><tr><th id="foo" class="name">{snip}<tbody><tr><td class="name">{snip}"
|
||||
|
||||
|
||||
``th`` and ``td`` are special cases because they're extended during rendering
|
||||
to add the column name as a class. This is done to make writing CSS easier.
|
||||
Have a look at each column's API reference to find which elements are
|
||||
supported.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
.. _column-headers:
|
||||
|
||||
Customising column headings
|
||||
===========================
|
||||
|
||||
The header cell for each column comes from `~.Column.header`. By default this
|
||||
method returns `~.Column.verbose_name`, falling back to the titlised attribute
|
||||
name of the column in the table class.
|
||||
|
||||
When using queryset data and a verbose name hasn't been explicitly
|
||||
defined for a column, the corresponding model field's verbose name will be
|
||||
used.
|
||||
|
||||
Consider the following:
|
||||
|
||||
>>> class Person(models.Model):
|
||||
... first_name = models.CharField(verbose_name='model verbose name', max_length=200)
|
||||
... last_name = models.CharField(max_length=200)
|
||||
... region = models.ForeignKey('Region')
|
||||
...
|
||||
>>> class Region(models.Model):
|
||||
... name = models.CharField(max_length=200)
|
||||
...
|
||||
>>> class PersonTable(tables.Table):
|
||||
... first_name = tables.Column()
|
||||
... ln = tables.Column(accessor='last_name')
|
||||
... region_name = tables.Column(accessor='region.name')
|
||||
...
|
||||
>>> table = PersonTable(Person.objects.all())
|
||||
>>> table.columns['first_name'].header
|
||||
u'Model Verbose Name'
|
||||
>>> table.columns['ln'].header
|
||||
u'Last Name'
|
||||
>>> table.columns['region_name'].header
|
||||
u'Name'
|
||||
|
||||
As you can see in the last example (region name), the results are not always
|
||||
desirable when an accessor is used to cross relationships. To get around this
|
||||
be careful to define `.Column.verbose_name`.
|
|
@ -0,0 +1,142 @@
|
|||
.. _custom-rendering:
|
||||
|
||||
Custom rendering
|
||||
================
|
||||
|
||||
Various options are available for changing the way the table is :term:`rendered
|
||||
<render>`. Each approach has a different balance of ease-of-use and
|
||||
flexibility.
|
||||
|
||||
|
||||
.. _table.render_foo:
|
||||
|
||||
:meth:`Table.render_FOO` methods
|
||||
--------------------------------
|
||||
|
||||
To change how a column is rendered, implement a ``render_FOO`` method on the
|
||||
table (where ``FOO`` is the :term:`column name`). This approach is suitable if
|
||||
you have a one-off change that you don't want to use in multiple tables.
|
||||
|
||||
Supported keyword arguments include:
|
||||
|
||||
- ``record`` -- the entire record for the row from the :term:`table data`
|
||||
- ``value`` -- the value for the cell retrieved from the :term:`table data`
|
||||
- ``column`` -- the `.Column` object
|
||||
- ``bound_column`` -- the `.BoundColumn` object
|
||||
- ``bound_row`` -- the `.BoundRow` object
|
||||
- ``table`` -- alias for ``self``
|
||||
|
||||
Here's an example where the first column displays the current row number::
|
||||
|
||||
>>> import django_tables2 as tables
|
||||
>>> import itertools
|
||||
>>> class SimpleTable(tables.Table):
|
||||
... row_number = tables.Column(empty_values=())
|
||||
... id = tables.Column()
|
||||
... age = tables.Column()
|
||||
...
|
||||
... def __init__(self, *args, **kwargs):
|
||||
... super(SimpleTable, self).__init__(*args, **kwargs)
|
||||
... self.counter = itertools.count()
|
||||
...
|
||||
... def render_row_number(self):
|
||||
... return 'Row %d' % next(self.counter)
|
||||
...
|
||||
... def render_id(self, value):
|
||||
... return '<%s>' % value
|
||||
...
|
||||
>>> table = SimpleTable([{'age': 31, 'id': 10}, {'age': 34, 'id': 11}])
|
||||
>>> for cell in table.rows[0]:
|
||||
... print cell
|
||||
...
|
||||
Row 0
|
||||
<10>
|
||||
31
|
||||
|
||||
Python's `inspect.getargspec` is used to only pass the arguments declared by the
|
||||
function. This means it's not necessary to add a catch all (``**``) keyword
|
||||
argument.
|
||||
|
||||
.. important::
|
||||
|
||||
`render` methods are *only* called if the value for a cell is determined to
|
||||
be not an :term:`empty value`. When a value is in `.Column.empty_values`,
|
||||
a default value is rendered instead (both `.Column.render` and
|
||||
``Table.render_FOO`` are skipped).
|
||||
|
||||
.. _subclassing-column:
|
||||
|
||||
Subclassing `.Column`
|
||||
---------------------
|
||||
|
||||
Defining a column subclass allows functionality to be reused across tables.
|
||||
Columns have a `render` method that behaves the same as :ref:`table.render_foo`
|
||||
methods on tables::
|
||||
|
||||
>>> import django_tables2 as tables
|
||||
>>>
|
||||
>>> class UpperColumn(tables.Column):
|
||||
... def render(self, value):
|
||||
... return value.upper()
|
||||
...
|
||||
>>> class Example(tables.Table):
|
||||
... normal = tables.Column()
|
||||
... upper = UpperColumn()
|
||||
...
|
||||
>>> data = [{'normal': 'Hi there!',
|
||||
... 'upper': 'Hi there!'}]
|
||||
...
|
||||
>>> table = Example(data)
|
||||
>>> table.as_html()
|
||||
u'<table><thead><tr><th>Normal</th><th>Upper</th></tr></thead><tbody><tr><td>Hi there!</td><td>HI THERE!</td></tr></tbody></table>\n'
|
||||
|
||||
See :ref:`table.render_foo` for a list of arguments that can be accepted.
|
||||
|
||||
For complicated columns, you may want to return HTML from the
|
||||
:meth:`~Column.render` method. This is fine, but be sure to mark the string as
|
||||
safe to avoid it being escaped::
|
||||
|
||||
>>> from django.utils.safestring import mark_safe
|
||||
>>> from django.utils.html import escape
|
||||
>>>
|
||||
>>> class ImageColumn(tables.Column):
|
||||
... def render(self, value):
|
||||
... return mark_safe('<img src="/media/img/%s.jpg" />'
|
||||
... % escape(value))
|
||||
...
|
||||
|
||||
|
||||
.. _css:
|
||||
|
||||
CSS
|
||||
---
|
||||
|
||||
In order to use CSS to style a table, you'll probably want to add a
|
||||
``class`` or ``id`` attribute to the ``<table>`` element. django-tables2 has
|
||||
a hook that allows abitrary attributes to be added to the ``<table>`` tag.
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
>>> import django_tables2 as tables
|
||||
>>> class SimpleTable(tables.Table):
|
||||
... id = tables.Column()
|
||||
... age = tables.Column()
|
||||
...
|
||||
... class Meta:
|
||||
... attrs = {'class': 'mytable'}
|
||||
...
|
||||
>>> table = SimpleTable()
|
||||
>>> table.as_html()
|
||||
'<table class="mytable">...'
|
||||
|
||||
.. _custom-template:
|
||||
|
||||
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
|
||||
`.Table` subclass into your own template, and render it yourself.
|
||||
|
||||
Have a look at the ``django_tables2/table.html`` template for an example.
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
Class Based Generic Mixins
|
||||
==========================
|
||||
|
||||
Django 1.3 introduced `class based views`__ as a mechanism to reduce the
|
||||
repetition in view code. django-tables2 comes with a single class based view
|
||||
mixin: `.SingleTableMixin`. It makes it trivial to incorporate a table into a
|
||||
view/template.
|
||||
|
||||
The following view parameters are supported:
|
||||
|
||||
- ``table_class`` –- the table class to use, e.g. ``SimpleTable``
|
||||
- ``table_data`` (or ``get_table_data()``) -- the data used to populate the table
|
||||
- ``context_table_name`` -- the name of template variable containing the table object
|
||||
- ``table_pagination`` -- pagination options to pass to `.RequestConfig`
|
||||
|
||||
.. __: https://docs.djangoproject.com/en/1.3/topics/class-based-views/
|
||||
|
||||
For example:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
from django_tables2 import SingleTableView
|
||||
|
||||
|
||||
class Person(models.Model):
|
||||
first_name = models.CharField(max_length=200)
|
||||
last_name = models.CharField(max_length=200)
|
||||
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
class Meta:
|
||||
model = Simple
|
||||
|
||||
|
||||
class PersonList(SingleTableView):
|
||||
model = Person
|
||||
table_class = PersonTable
|
||||
|
||||
|
||||
The template could then be as simple as:
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{% load render_table from django_tables2 %}
|
||||
{% render_table table %}
|
||||
|
||||
Such little code is possible due to the example above taking advantage of
|
||||
default values and `.SimpleTableMixin`'s eagarness at finding data sources
|
||||
when one isn't explicitly defined.
|
||||
|
||||
.. note::
|
||||
|
||||
If you need more than one table on a page, use `.SingleTableView` and use
|
||||
`.get_context_data` to initialise the other tables and add them to the
|
||||
context.
|
||||
|
||||
.. note::
|
||||
|
||||
You don't have to base your view on `ListView`, you're able to mix
|
||||
`SingleTableMixin` directly.
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
Glossary
|
||||
========
|
||||
|
||||
.. glossary::
|
||||
|
||||
accessor
|
||||
Refers to an `.Accessor` object
|
||||
|
||||
column name
|
||||
The name given to a column. In the follow example, the *column name* is
|
||||
``age``.
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
class SimpleTable(tables.Table):
|
||||
age = tables.Column()
|
||||
|
||||
empty value
|
||||
An empty value is synonymous with "no value". Columns have an
|
||||
``empty_values`` attribute that contains values that are considered
|
||||
empty. It's a way to declare which values from the database correspond
|
||||
to *null*/*blank*/*missing* etc.
|
||||
|
||||
order by alias
|
||||
A prefixed column name that describes how a column should impact the
|
||||
order of data within the table. This allows the implementation of how
|
||||
a column affects ordering to be abstracted, which is useful (e.g. in
|
||||
querystrings).
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
class ExampleTable(tables.Table):
|
||||
name = tables.Column(order_by=('first_name', 'last_name'))
|
||||
|
||||
In this example ``-name`` and ``name`` are valid order by aliases. In
|
||||
a querystring you might then have ``?order=-name``.
|
||||
|
||||
table
|
||||
The traditional concept of a table. i.e. a grid of rows and columns
|
||||
containing data.
|
||||
|
||||
view
|
||||
A Django view.
|
||||
|
||||
record
|
||||
A single Python object used as the data for a single row.
|
||||
|
||||
render
|
||||
The act of serialising a `.Table` into
|
||||
HTML.
|
||||
|
||||
template
|
||||
A Django template.
|
||||
|
||||
table data
|
||||
An interable of :term:`records <record>` that
|
||||
`.Table` uses to populate its rows.
|
|
@ -0,0 +1,50 @@
|
|||
.. _localization-control:
|
||||
|
||||
Controlling localization
|
||||
========================
|
||||
|
||||
.. note::
|
||||
This functionality doesn't work in Django prior to version 1.3
|
||||
|
||||
Django_tables2 allows you to define which column of a table should or should not
|
||||
be localized. For example you may want to use this feature in following use cases:
|
||||
|
||||
* You want to format some columns representing for example numeric values in the given locales
|
||||
even if you don't enable `USE_L10N` in your settings file.
|
||||
|
||||
* You don't want to format primary key values in your table
|
||||
even if you enabled `USE_L10N` in your settings file.
|
||||
|
||||
This control is done by using two filter functions in Django's `l10n` library
|
||||
named `localize` and `unlocalize`. Check out Django docs about
|
||||
:ref:`localization <django:format-localization>` for more information about them.
|
||||
|
||||
There are two ways of controling localization in your columns.
|
||||
|
||||
First one is setting the `~.Column.localize` attribute in your column definition
|
||||
to `True` or `False`. Like so::
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
id = tables.Column(name="id", accessor="pk", localize=False)
|
||||
class Meta:
|
||||
model = Person
|
||||
|
||||
|
||||
.. note::
|
||||
The default value of the `localize` attribute is `None` which means the formatting
|
||||
of columns is dependant from the `USE_L10N` setting.
|
||||
|
||||
The second way is to define a `~.Table.Meta.localize` and/or `~.Table.Meta.unlocalize`
|
||||
tuples in your tables Meta class (jutst like with `~.Table.Meta.fields`
|
||||
or `~.Table.Meta.exclude`). You can do this like so::
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
id = tables.Column(accessor="pk")
|
||||
value = tables.Column(accessor="some_numerical_field")
|
||||
class Meta:
|
||||
model = Person
|
||||
unlocalize = ('id',)
|
||||
localize = ('value',)
|
||||
|
||||
If you define the same column in both `localize` and `unlocalize` then the value
|
||||
of this column will be "unlocalized" which means that `unlocalize` has higher precedence.
|
|
@ -0,0 +1,48 @@
|
|||
.. _order-by-accessors:
|
||||
|
||||
Specifying alternative ordering for a column
|
||||
============================================
|
||||
|
||||
When using queryset data, it's possible for a column to present a computed
|
||||
value that doesn't correspond to a column in the database. In this situation
|
||||
attempting to order the column will cause a database exception.
|
||||
|
||||
Example::
|
||||
|
||||
# models.py
|
||||
class Person(models.Model):
|
||||
first_name = models.CharField(max_length=200)
|
||||
family_name = models.CharField(max_length=200)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return u"%s %s" % (self.first_name, self.family_name)
|
||||
|
||||
# tables.py
|
||||
class PersonTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
::
|
||||
|
||||
>>> table = PersonTable(Person.objects.all())
|
||||
>>> table.order_by = "name"
|
||||
>>> table.as_html()
|
||||
...
|
||||
FieldError: Cannot resolve keyword u'name' into field. Choices are: first_name, family_name
|
||||
|
||||
The solution is to declare which fields should be used when ordering on via the
|
||||
``order_by`` argument::
|
||||
|
||||
# tables.py
|
||||
class PersonTable(tables.Table):
|
||||
name = tables.Column(order_by=("first_name", "family_name"))
|
||||
|
||||
Accessor syntax can be used for the values, but they must terminate on a model
|
||||
field.
|
||||
|
||||
If ordering doesn't make sense for a particular column, it can be disabled via
|
||||
the ``orderable`` argument::
|
||||
|
||||
class SimpleTable(tables.Table):
|
||||
name = tables.Column()
|
||||
actions = tables.Column(orderable=False)
|
|
@ -0,0 +1,24 @@
|
|||
.. _pagination:
|
||||
|
||||
Pagination
|
||||
==========
|
||||
|
||||
Pagination is easy, just call :meth:`.Table.paginate` and
|
||||
pass in the current page number, e.g.
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def people_listing(request):
|
||||
table = PeopleTable(Person.objects.all())
|
||||
table.paginate(page=request.GET.get('page', 1), per_page=25)
|
||||
return render(request, 'people_listing.html', {'table': table})
|
||||
|
||||
If you're using `.RequestConfig`, pass pagination options to the constructor,
|
||||
e.g.:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def people_listing(request):
|
||||
table = PeopleTable(Person.objects.all())
|
||||
RequestConfig(request, paginate={"per_page": 25}).configure(table)
|
||||
return render(request, 'people_listing.html', {'table': table})
|
|
@ -0,0 +1,34 @@
|
|||
.. _query-string-fields:
|
||||
|
||||
Querystring fields
|
||||
==================
|
||||
|
||||
Tables pass data via the querystring to indicate ordering and pagination
|
||||
preferences.
|
||||
|
||||
The names of the querystring variables are configurable via the options:
|
||||
|
||||
- ``order_by_field`` -- default: ``"sort"``
|
||||
- ``page_field`` -- default: ``"page"``
|
||||
- ``per_page_field`` -- default: ``"per_page"``, **note:** this field currently
|
||||
isn't used by ``{% render_table %}``
|
||||
|
||||
Each of these can be specified in three places:
|
||||
|
||||
- ``Table.Meta.foo``
|
||||
- ``Table(..., foo=...)``
|
||||
- ``Table(...).foo = ...``
|
||||
|
||||
If you're using multiple tables on a single page, you'll want to prefix these
|
||||
fields with a table-specific name. e.g.
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def people_listing(request):
|
||||
config = RequestConfig(request)
|
||||
table1 = PeopleTable(Person.objects.all(), prefix="1-") # prefix specified
|
||||
table2 = PeopleTable(Person.objects.all(), prefix="2-") # prefix specified
|
||||
config.configure(table1)
|
||||
config.configure(table2)
|
||||
return render(request, "people_listing.html",
|
||||
{"table1": table1, "table2": table2})
|
|
@ -0,0 +1,21 @@
|
|||
.. _swapping-columns:
|
||||
|
||||
Swapping the position of columns
|
||||
================================
|
||||
|
||||
By default columns are positioned in the same order as they are declared,
|
||||
however when mixing auto-generated columns (via `Table.Meta.model`) with
|
||||
manually declared columns, the column sequence becomes ambiguous.
|
||||
|
||||
To resolve the ambiguity, columns sequence can be declared via the
|
||||
`.Table.Meta.sequence` option::
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
selection = tables.CheckBoxColumn(accessor="pk", orderable=False)
|
||||
|
||||
class Meta:
|
||||
model = Person
|
||||
sequence = ("selection", "first_name", "last_name")
|
||||
|
||||
The special value ``"..."`` can be used to indicate that any omitted columns
|
||||
should inserted at that location. As such it can be used at most once.
|
|
@ -0,0 +1,48 @@
|
|||
.. _table-data:
|
||||
|
||||
Populating a table with data
|
||||
============================
|
||||
|
||||
Tables are compatible with a range of input data structures. If you've seen the
|
||||
tutorial you'll have seen a queryset being used, however any iterable that
|
||||
supports :func:`len` and contains items that expose key-based accessed to
|
||||
column values is fine.
|
||||
|
||||
An an example we'll demonstrate using list of dicts. When defining a table it's
|
||||
necessary to declare each column. If your data matches the fields in a model,
|
||||
columns can be declared automatically for you via the `Table.Meta.model`
|
||||
option, but for non-queryset data you'll probably want to declare
|
||||
them manually::
|
||||
|
||||
import django_tables2 as tables
|
||||
|
||||
data = [
|
||||
{"name": "Bradley"},
|
||||
{"name": "Stevie"},
|
||||
]
|
||||
|
||||
class NameTable(tables.Table):
|
||||
name = tables.Column()
|
||||
|
||||
table = NameTable(data)
|
||||
|
||||
You can use this technique to override columns that were automatically created
|
||||
via `Table.Meta.model` too::
|
||||
|
||||
# models.py
|
||||
from django.db import models
|
||||
|
||||
class Person(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
|
||||
|
||||
# tables.py
|
||||
import django_tables2 as tables
|
||||
from .models import Person
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
name = tables.Column(verbose_name="full name")
|
||||
|
||||
class Meta:
|
||||
model = Person
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
Table Mixins
|
||||
============
|
||||
|
||||
It's possible to create a mixin for a table that overrides something, however
|
||||
unless it itself is a subclass of `.Table` class variable instances of
|
||||
`.Column` will **not** be added to the class which is using the mixin.
|
||||
|
||||
Example::
|
||||
|
||||
>>> class UselessMixin(object):
|
||||
... extra = tables.Column()
|
||||
...
|
||||
>>> class TestTable(UselessMixin, tables.Table):
|
||||
... name = tables.Column()
|
||||
...
|
||||
>>> TestTable.base_columns.keys()
|
||||
['name']
|
||||
|
||||
To have a mixin contribute a column, it needs to be a subclass of
|
||||
`~django_tables2.tables.Table`. With this in mind the previous example
|
||||
*should* have been written as follows::
|
||||
|
||||
>>> class UsefulMixin(tables.Table):
|
||||
... extra = tables.Column()
|
||||
...
|
||||
>>> class TestTable(UsefulMixin, tables.Table):
|
||||
... name = tables.Column()
|
||||
...
|
||||
>>> TestTable.base_columns.keys()
|
||||
['extra', 'name']
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
.. _tables-for-models:
|
||||
|
||||
Tables for models
|
||||
=================
|
||||
|
||||
If you build use tables to display `.QuerySet` data, rather than defining each
|
||||
column manually in the table, the `.Table.Meta.model` option allows tables to
|
||||
be dynamically created based on a model::
|
||||
|
||||
# models.py
|
||||
class Person(models.Model):
|
||||
first_name = models.CharField(max_length=200)
|
||||
last_name = models.CharField(max_length=200)
|
||||
user = models.ForeignKey("auth.User")
|
||||
dob = models.DateField()
|
||||
|
||||
# tables.py
|
||||
class PersonTable(tables.Table):
|
||||
class Meta:
|
||||
model = Person
|
||||
|
||||
This has a number of benefits:
|
||||
|
||||
- Less code, easier to write, more DRY
|
||||
- Columns use the field's `~.models.Field.verbose_name`
|
||||
- Specialised columns are used where possible (e.g. `.DateColumn` for a
|
||||
`~.models.DateField`)
|
||||
|
||||
When using this approach, the following options are useful:
|
||||
|
||||
- `~.Table.Meta.sequence` -- reorder columns
|
||||
- `~.Table.Meta.fields` -- specify model fields to *include*
|
||||
- `~.Table.Meta.exclude` -- specify model fields to *exclude*
|
|
@ -0,0 +1,19 @@
|
|||
Template filters
|
||||
================
|
||||
|
||||
title
|
||||
-----
|
||||
|
||||
String filter that performs title case conversion on a per-word basis, leaving
|
||||
words containing upper-case letters alone.
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{{ "start 6PM"|title }} # Start 6PM
|
||||
{{ "sTart 6pm"|title }} # sTart 6pm
|
||||
|
||||
.. warning::
|
||||
|
||||
Be careful when loading the ``django_tables2`` template library to not
|
||||
in advertantly load ``title``. You should always use the
|
||||
``{% load ... from ... %}`` syntax.
|
|
@ -0,0 +1,66 @@
|
|||
.. _template_tags:
|
||||
|
||||
Template tags
|
||||
=============
|
||||
|
||||
.. _template-tags.render_table:
|
||||
|
||||
render_table
|
||||
------------
|
||||
|
||||
Renders a `~django_tables2.tables.Table` object to HTML and enables as
|
||||
many features in the output as possible.
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{% load django_tables2 %}
|
||||
{% render_table table %}
|
||||
|
||||
{# Alternatively a specific template can be used #}
|
||||
{% render_table table "path/to/custom_table_template.html" %}
|
||||
|
||||
If the second argument (template path) is given, the template will be rendered
|
||||
with a `.RequestContext` and the table will be in the variable ``table``.
|
||||
|
||||
.. note::
|
||||
|
||||
This tag temporarily modifies the `.Table` object during rendering. A
|
||||
``context`` attribute is added to the table, providing columns with access
|
||||
to the current context for their own rendering (e.g. `.TemplateColumn`).
|
||||
|
||||
This tag requires that the template in which it's rendered contains the
|
||||
`~.http.HttpRequest` inside a ``request`` variable. This can be achieved by
|
||||
ensuring the ``TEMPLATE_CONTEXT_PROCESSORS`` setting contains
|
||||
``"django.core.context_processors.request"``. By default it is not included,
|
||||
and the setting itself is not even defined within your project's
|
||||
``settings.py``. To resolve this add the following to your ``settings.py``:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS
|
||||
TEMPLATE_CONTEXT_PROCESSORS += ('django.core.context_processors.request',)
|
||||
|
||||
|
||||
.. _template-tags.querystring:
|
||||
|
||||
querystring
|
||||
-----------
|
||||
|
||||
A utility that allows you to update a portion of the query-string without
|
||||
overwriting the entire thing.
|
||||
|
||||
Let's assume we have the querystring ``?search=pirates&sort=name&page=5`` and
|
||||
we want to update the ``sort`` parameter:
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{% querystring "sort"="dob" %} # ?search=pirates&sort=dob&page=5
|
||||
{% querystring "sort"="" %} # ?search=pirates&page=5
|
||||
{% querystring "sort"="" "search"="" %} # ?page=5
|
||||
|
||||
{% with "search" as key %} # supports variables as keys
|
||||
{% querystring key="robots" %} # ?search=robots&page=5
|
||||
{% endwith %}
|
||||
|
||||
This tag requires the ``django.core.context_processors.request`` context
|
||||
processor, see :ref:`template-tags.render_table`.
|
|
@ -0,0 +1,97 @@
|
|||
Tutorial
|
||||
========
|
||||
|
||||
1. ``pip install django-tables2``
|
||||
2. Add ``'django_tables2'`` to ``INSTALLED_APPS``
|
||||
3. Add ``'django.core.context_processors.request'`` to ``TEMPLATE_CONTEXT_PROCESSORS``
|
||||
|
||||
We're going to run through creating a tutorial app. Let's start with a simple model::
|
||||
|
||||
# tutorial/models.py
|
||||
class Person(models.Model):
|
||||
name = models.CharField(verbose_name="full name")
|
||||
|
||||
Add some data so you have something to display in the table. Now write a view
|
||||
to pass a ``Person`` queryset into a template::
|
||||
|
||||
# tutorial/views.py
|
||||
from django.shortcuts import render
|
||||
|
||||
def people(request):
|
||||
return render(request, "people.html", {"people": Person.objects.all()})
|
||||
|
||||
Finally, implement the template:
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{# tutorial/templates/people.html #}
|
||||
{% load render_table from django_tables2 %}
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="{{ STATIC_URL }}django_tables2/themes/paleblue/css/screen.css" />
|
||||
</head>
|
||||
<body>
|
||||
{% render_table people %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Hook the view up in your URLs, and load the page, you should see:
|
||||
|
||||
.. figure:: /_static/tutorial.png
|
||||
:align: center
|
||||
:alt: An example table rendered using django-tables2
|
||||
|
||||
While simple, passing a queryset directly to ``{% render_table %}`` doesn't
|
||||
allow for any customisation. For that, you must define a `.Table` class.
|
||||
|
||||
::
|
||||
|
||||
# tutorial/tables.py
|
||||
import django_tables2 as tables
|
||||
from tutorial.models import Person
|
||||
|
||||
class PersonTable(tables.Table):
|
||||
class Meta:
|
||||
model = Person
|
||||
# add class="paleblue" to <table> tag
|
||||
attrs = {"class": "paleblue"}
|
||||
|
||||
|
||||
You'll then need to instantiate and configure the table in the view, before
|
||||
adding it to the context.
|
||||
|
||||
::
|
||||
|
||||
# tutorial/views.py
|
||||
from django.shortcuts import render
|
||||
from django_tables2 import RequestConfig
|
||||
from tutorial.models import Person
|
||||
from tutorial.tables import PersonTable
|
||||
|
||||
def people(request):
|
||||
table = PersonTable(Person.objects.all())
|
||||
RequestConfig(request).configure(table)
|
||||
return render(request, 'people.html', {'table': table})
|
||||
|
||||
Using `.RequestConfig` automatically pulls values from ``request.GET`` and
|
||||
updates the table accordingly. This enables data ordering and pagination.
|
||||
|
||||
Rather than passing a queryset to ``{% render_table %}``, instead pass the
|
||||
table.
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{% render_table table %}
|
||||
|
||||
.. note::
|
||||
|
||||
``{% render_table %}`` works best when it's used in a template that
|
||||
contains the current request in the context as ``request``. The easiest way
|
||||
to enable this, is to ensure that the ``TEMPLATE_CONTEXT_PROCESSORS``
|
||||
setting contains ``"django.core.context_processors.request"``.
|
||||
|
||||
At this point you haven't actually customised anything, you've merely added the
|
||||
boilerplate code that ``{% render_table %}`` does for you when given a
|
||||
queryset. The remaining sections in this document describe how to change
|
||||
various aspects of the table.
|
|
@ -0,0 +1,62 @@
|
|||
Upgrading from django-tables Version 1
|
||||
======================================
|
||||
|
||||
- Change your ``INSTALLLED_APPS`` entry from ``"django_tables.app"`` to
|
||||
``"django_tables2"``.
|
||||
|
||||
- Change all your import references from ``django_tables`` to
|
||||
``django_tables2``.
|
||||
|
||||
- Replace all references to the old ``MemoryTable`` and ``ModelTable``
|
||||
classes with simply ``Table``.
|
||||
|
||||
- In your templates, load the ``django_tables2`` template library;
|
||||
``{% load django_tables2 %}`` instead of ``{% load tables %}``.
|
||||
|
||||
- A table object is no longer iterable; rather than ``for row in table``,
|
||||
instead you now do explicitly: ``for row in table.rows``.
|
||||
|
||||
- If you were using ``row.data`` to access a row's underlying data,
|
||||
replace it with ``row.record`` instead.
|
||||
|
||||
- When declaring columns, replace the use of::
|
||||
|
||||
name_in_dataset = tables.Column(name="wanted_column_name")
|
||||
|
||||
with::
|
||||
|
||||
wanted_column_name = tables.Column(accessor="name_in_dataset")
|
||||
|
||||
- When declaring columns, replace the use of::
|
||||
|
||||
column_to_override = tables.Column(name="wanted_column_name", data="name_in_dataset")
|
||||
|
||||
with::
|
||||
|
||||
wanted_column_name = tables.Column(accessor="name_in_dataset")
|
||||
|
||||
and exclude ``column_to_override`` via the table meta data.
|
||||
|
||||
- When generating the link to order the column, instead of:
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{% set_url_param sort=column.name_toggled %}
|
||||
|
||||
use:
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{% querystring table.order_by_field=column.order_by_alias.next %}
|
||||
|
||||
- Replace:
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{{ column.is_ordered_reverse }} and {{ column.is_ordered_straight }}
|
||||
|
||||
with:
|
||||
|
||||
.. sourcecode:: django
|
||||
|
||||
{{ column.order_by.is_descending }} and {{ column.order_by.is_ascending }}
|
Loading…
Reference in New Issue