/MANIFEST
/dist
+/docs/_build/*
/BRANCH_TODO
+# Project files
/.project
/.pydevproject
/*.wpr
-django-tables\r
-=============\r
-\r
-A Django QuerySet renderer.\r
-\r
-Installation\r
-------------\r
-\r
-Adding django-tables to your INSTALLED_APPS settings is optional, it'll get\r
-you the ability to load some template utilities via {% load tables %}, but\r
-apart from that, ``import django_tables as tables`` should get you going.\r
-\r
-Running the test suite\r
-----------------------\r
-\r
-The test suite uses nose:\r
- http://somethingaboutorange.com/mrl/projects/nose/\r
-\r
-Working with Tables\r
--------------------\r
-\r
-A table class looks very much like a form:\r
-\r
- import django_tables as tables\r
- class CountryTable(tables.MemoryTable):\r
- name = tables.Column(verbose_name="Country Name")\r
- population = tables.Column(sortable=False, visible=False)\r
- time_zone = tables.Column(name="tz", default="UTC+1")\r
-\r
-Instead of fields, you declare a column for every piece of data you want to\r
-expose to the user.\r
-\r
-To use the table, create an instance:\r
-\r
- countries = CountryTable([{'name': 'Germany', population: 80},\r
- {'name': 'France', population: 64}])\r
-\r
-Decide how the table should be sorted:\r
-\r
- countries.order_by = ('name',)\r
- assert [row.name for row in countries.row] == ['France', 'Germany']\r
-\r
- countries.order_by = ('-population',)\r
- assert [row.name for row in countries.row] == ['Germany', 'France']\r
-\r
-If you pass the table object along into a template, you can do:\r
-\r
- {% for column in countries.columns %}\r
- {{column}}\r
- {% endfor %}\r
-\r
-Which will give you:\r
-\r
- Country Name\r
- Timezone\r
-\r
-Note that ``population`` is skipped (as it has ``visible=False``), that the\r
-declared verbose name for the ``name`` column is used, and that ``time_zone``\r
-is converted into a more beautiful string for output automatically.\r
-\r
-There are few requirements for the source data of a table. It should be an\r
-iterable with dict-like objects. Values found in the source data that are\r
-not associated with a column are ignored, missing values are replaced by\r
-the column default or None.\r
-\r
-Common Workflow\r
-~~~~~~~~~~~~~~~\r
-\r
-Usually, you are going to use a table like this. Assuming ``CountryTable``\r
-is defined as above, your view will create an instance and pass it to the\r
-template:\r
-\r
- def list_countries(request):\r
- data = ...\r
- countries = CountryTable(data, order_by=request.GET.get('sort'))\r
- return render_to_response('list.html', {'table': countries})\r
-\r
-Note that we are giving the incoming "sort" query string value directly to\r
-the table, asking for a sort. All invalid column names will (by default) be\r
-ignored. In this example, only "name" and "tz" are allowed, since:\r
-\r
- * "population" has sortable=False\r
- * "time_zone" has it's name overwritten with "tz".\r
-\r
-Then, in the "list.html" template, write:\r
-\r
- <table>\r
- <tr>\r
- {% for column in table.columns %}\r
- <th><a href="?sort={{ column.name }}">{{ column }}</a></th>\r
- {% endfor %}\r
- </tr>\r
- {% for row in table.rows %}\r
- <tr>\r
- {% for value in row %}\r
- <td>{{ value }}</td>\r
- {% endfor %}\r
- </tr>\r
- {% endfor %}\r
- </table>\r
-\r
-This will output the data as an HTML table. Note how the table is now fully\r
-sortable, since our link passes along the column name via the querystring,\r
-which in turn will be used by the server for ordering. ``order_by`` accepts\r
-comma-separated strings as input, and "{{ table.order_by }}" will be rendered\r
-as a such a string.\r
-\r
-Instead of the iterator, you can use your knowledge of the table structure to\r
-access columns directly:\r
-\r
- {% if table.columns.tz.visible %}\r
- {{ table.columns.tz }}\r
- {% endfor %}\r
-\r
-\r
-Dynamic Data\r
-~~~~~~~~~~~~\r
-\r
-If any value in the source data is a callable, it will be passed it's own\r
-row instance and is expected to return the actual value for this particular\r
-table cell.\r
-\r
-Similarily, the colunn default value may also be callable that will takes\r
-the row instance as an argument (representing the row that the default is\r
-needed for).\r
-\r
-\r
-Table Options\r
--------------\r
-\r
-Table-specific options are implemented using the same inner ``Meta`` class\r
-concept as known from forms and models in Django:\r
-\r
- class MyTable(tables.MemoryTable):\r
- class Meta:\r
- sortable = True\r
-\r
-Currently, for non-model tables, the only supported option is ``sortable``.\r
-Per default, all columns are sortable, unless a column specifies otherwise.\r
-This meta option allows you to overwrite the global default for the table.\r
-\r
-\r
-ModelTables\r
------------\r
-\r
-Like forms, tables can also be used with models:\r
-\r
- class CountryTable(tables.ModelTable):\r
- id = tables.Column(sortable=False, visible=False)\r
- class Meta:\r
- model = Country\r
- exclude = ['clicks']\r
-\r
-The resulting table will have one column for each model field, with the\r
-exception of "clicks", which is excluded. The column for "id" is overwritten\r
-to both hide it and deny it sort capability.\r
-\r
-When instantiating a ModelTable, you usually pass it a queryset to provide\r
-the table data:\r
-\r
- qs = Country.objects.filter(continent="europe")\r
- countries = CountryTable(qs)\r
-\r
-However, you can also just do:\r
-\r
- countries = CountryTable()\r
-\r
-and all rows exposed by the default manager of the model the table is based\r
-on will be used.\r
-\r
-If you are using model inheritance, then the following also works:\r
-\r
- countries = CountryTable(CountrySubclass)\r
-\r
-Note that while you can pass any model, it really only makes sense if the\r
-model also provides fields for the columns you have defined.\r
-\r
-If you just want to use ModelTables, but without auto-generated columns,\r
-you do not have to list all model fields in the ``exclude`` Meta option.\r
-Instead, simply don't specify a model.\r
-\r
-\r
-Custom Columns\r
-~~~~~~~~~~~~~~\r
-\r
-You an add custom columns to your ModelTable that are not based on actual\r
-model fields:\r
-\r
- class CountryTable(tables.ModelTable):\r
- custom = tables.Column(default="foo")\r
- class Meta:\r
- model = Country\r
-\r
-Just make sure your model objects do provide an attribute with that name.\r
-Functions are also supported, so ``Country.custom`` could be a callable.\r
-\r
-\r
-Spanning relationships\r
-~~~~~~~~~~~~~~~~~~~~~~\r
-\r
-Let's assume you have a ``Country`` model, with a foreignkey ``capital``\r
-pointing to the ``City`` model. While displaying a list of countries,\r
-you might want want to link to the capital's geographic location, which is\r
-stored in ``City.geo`` as a ``(lat, long)`` tuple, on Google Maps.\r
-\r
-ModelTables support relationship spanning syntax of Django's database api:\r
-\r
- class CountryTable(tables.ModelTable):\r
- city__geo = tables.Column(name="geo")\r
-\r
-This will add a column named "geo", based on the field by the same name\r
-from the "city" relationship. Note that the name used to define the column\r
-is what will be used to access the data, while the name-overwrite passed to\r
-the column constructor just defines a prettier name for us to work with.\r
-This is to be consistent with auto-generated columns based on model fields,\r
-where the field/column name naturally equals the source name.\r
-\r
-However, to make table defintions more visually appealing and easier to\r
-read, an alternative syntax is supported: setting the column ``data``\r
-property to the appropriate string.\r
-\r
- class CountryTable(tables.ModelTable):\r
- geo = tables.Column(data='city__geo')\r
-\r
-Note that you don't need to define a relationship's fields as separate\r
-columns if you already have a column for the relationship itself, i.e.:\r
-\r
- class CountryTable(tables.ModelTable):\r
- city = tables.Column()\r
-\r
- for country in countries.rows:\r
- print country.city.id\r
- print country.city.geo\r
- print country.city.founder.name\r
-\r
-\r
-ModelTable Specialities\r
-~~~~~~~~~~~~~~~~~~~~~~~\r
-\r
-ModelTables currently have some restrictions with respect to ordering:\r
-\r
- * Custom columns not based on a model field do not support ordering,\r
- regardless of the ``sortable`` property (it is ignored).\r
-\r
- * A ModelTable column's ``default`` or ``data`` value does not affect\r
- ordering. This differs from the non-model table behaviour.\r
-\r
-If a column is mapped to a method on the model, that method will be called\r
-without arguments. This behavior differs from non-model tables, where a\r
-row object will be passed.\r
-\r
-If you are using callables (e.g. for the ``default`` or ``data`` column\r
-options), they will generally be run when a row is accessed, and\r
-possible repeatetly when accessed more than once. This behavior differs from\r
-non-model tables, where they would be called once, when the table is\r
-generated.\r
-\r
-Columns\r
--------\r
-\r
-Columns are what defines a table. Therefore, the way you configure your\r
-columns determines to a large extend how your table operates.\r
-\r
-``django_tables.columns`` currently defines three classes, ``Column``,\r
-``TextColumn`` and ``NumberColumn``. However, the two subclasses currently\r
-don't do anything special at all, so you can simply use the base class.\r
-While this will likely change in the future (e.g. when grouping is added),\r
-the base column class will continue to work by itself.\r
-\r
-There are no required arguments. The following is fine:\r
-\r
- class MyTable(tables.MemoryTable):\r
- c = tables.Column()\r
-\r
-It will result in a column named "c" in the table. You can specify the\r
-``name`` to override this:\r
-\r
- c = tables.Column(name="count")\r
-\r
-The column is now called and accessed via "count", although the table will\r
-still use "c" to read it's values from the source. You can however modify\r
-that as well, by specifying ``data``:\r
-\r
- c = tables.Column(name="count", data="count")\r
-\r
-For most practicual purposes, "c" is now meaningless. While in most cases\r
-you will just define your column using the name you want it to have, the\r
-above is useful when working with columns automatically generated from\r
-models:\r
-\r
- class BookTable(tables.ModelTable):\r
- book_name = tables.Column(name="name")\r
- author = tables.Column(data="info__author__name")\r
- class Meta:\r
- model = Book\r
-\r
-The overwritten ``book_name`` field/column will now be exposed as the\r
-cleaner "name", and the new "author" column retrieves it's values from\r
-``Book.info.author.name``.\r
-\r
-Note: ``data`` may also be a callable which will be passed a row object.\r
-\r
-Apart from their internal name, you can define a string that will be used\r
-when for display via ``verbose_name``:\r
-\r
- pubdate = tables.Column(verbose_name="Published")\r
-\r
-The verbose name will be used, for example, if you put in a template:\r
-\r
- {{ column }}\r
-\r
-If you don't want a column to be sortable by the user:\r
-\r
- pubdate = tables.Column(sortable=False)\r
-\r
-Sorting is also affected by ``direction``, which can be used to change the\r
-*default* sort direction to descending. Note that this option only indirectly\r
-translates to the actual direction. Normal und reverse order, the terms\r
-django-tables exposes, now simply mean different things.\r
-\r
- pubdate = tables.Column(direction='desc')\r
-\r
-If you don't want to expose a column (but still require it to exist, for\r
-example because it should be sortable nonetheless):\r
-\r
- pubdate = tables.Column(visible=False)\r
-\r
-The column and it's values will now be skipped when iterating through the\r
-table, although it can still be accessed manually.\r
-\r
-Finally, you can specify default values for your columns:\r
-\r
- health_points = tables.Column(default=100)\r
-\r
-Note that how the default is used and when it is applied differs between\r
-static and ModelTables.\r
-\r
-\r
-The table.columns container\r
----------------------------\r
-\r
-While you can iterate through ``columns`` and get all the currently visible\r
-columns, it further provides features that go beyond a simple iterator.\r
-\r
-You can access all columns, regardless of their visibility, through\r
-``columns.all``.\r
-\r
-``columns.sortable`` is a handy shortcut that exposes all columns which's\r
-``sortable`` attribute is True. This can be very useful in templates, when\r
-doing {% if column.sortable %} can conflict with {{ forloop.last }}.\r
-\r
-\r
-Tables and Pagination\r
----------------------\r
-\r
-If your table has a large number of rows, you probably want to paginate\r
-the output. There are two distinct approaches.\r
-\r
-First, you can just paginate over ``rows`` as you would do with any other\r
-data:\r
-\r
- table = MyTable(queryset)\r
- paginator = Paginator(table.rows, 10)\r
- page = paginator.page(1)\r
-\r
-You're not necessarily restricted to Django's own paginator (or subclasses) -\r
-any paginator should work with this approach, so long it only requires\r
-``rows`` to implement ``len()``, slicing, and, in the case of ModelTables, a\r
-``count()`` method. The latter means that the ``QuerySetPaginator`` also\r
-works as expected.\r
-\r
-Alternatively, you may use the ``paginate`` feature:\r
-\r
- table = MyTable(queryset)\r
- table.paginate(Paginator, 10, page=1, orphans=2)\r
- for row in table.rows.page():\r
- pass\r
- table.paginator # new attributes\r
- table.page\r
-\r
-The table will automatically create an instance of ``QuerySetPaginator``,\r
-passing it's own data as the first argument and additionally any arguments\r
-you have specified, except for ``page``. You may use any paginator, as long\r
-as it follows the Django protocol:\r
-\r
- * Take data as first argument.\r
- * Support a page() method returning an object with an ``object_list``\r
- attribute, exposing the paginated data.\r
-\r
-Note that due to the abstraction layer that django-tables represents, it is\r
-not necessary to use Django's QuerySetPaginator with ModelTables. Since the\r
-table knows that it holds a queryset, it will automatically choose to use\r
-count() to determine the data length (which is exactly what\r
-QuerySetPaginator would do).\r
-\r
-Ordering\r
---------\r
-\r
-The syntax is similar to that of the Django database API. Order may be\r
-specified a list (or tuple) of column names. If prefixed with a hyphen, the\r
-ordering for that particular column will be in reverse order.\r
-\r
-Random ordering is currently not supported.\r
-\r
-Interacting with order\r
-~~~~~~~~~~~~~~~~~~~~~~\r
-\r
-Letting the user change the order of a table is a common scenario. With\r
-respect to Django, this means adding links to your table output that will\r
-send off the appropriate arguments to the server. django-tables attempts\r
-to help with you that.\r
-\r
-A bound column, that is a colum accessed through a table instance, provides\r
-the following attributes:\r
-\r
- - ``name_reversed`` will simply return the column name prefixed with a\r
- hyphen; this is useful in templates, where string concatenation can\r
- at times be difficult.\r
-\r
- - ``name_toggled`` checks the tables current order, and will then\r
- return the column either prefixed with an hyphen (for reverse ordering)\r
- or without, giving you the exact opposite order. If the column is\r
- currently not ordered, it will start off in non-reversed order.\r
-\r
-It is easy to be confused about the difference between the ``reverse`` and\r
-``toggle`` terminology. django-tables tries to put normal/reverse-order\r
-abstraction on top of "ascending/descending", where as normal order could\r
-potentially mean either ascending or descending, depending on the column.\r
-\r
-Commonly, you see tables that indicate what columns they are currently\r
-ordered by using little arrows. To implement this:\r
-\r
- - ``is_ordered``: Returns True if the column is in the current\r
- ``order_by``, regardless of the polarity.\r
-\r
- - ``is_ordered_reverse``, ``is_ordered_straight``: Returns True if the\r
- column is ordered in reverse or non-reverse, respectively, otherwise\r
- False.\r
-\r
-The above is usually enough for most simple cases, where tables are only\r
-ordered by a single column. For scenarios in which multi-column order is\r
-used, additional attributes are available:\r
-\r
- - ``order_by``: Return the current order, but with the current column\r
- set to normal ordering. If the current column is not already part of\r
- the order, it is appended. Any existing columns in the order are\r
- maintained as-is.\r
-\r
- - ``order_by_reversed``, ``order_by_toggled``: Similarly, return the\r
- table's current ``order_by`` with the column set to reversed or toggled,\r
- respectively. Again, it is appended if not already ordered.\r
-\r
-Additionally, ``table.order_by.toggle()`` may also be useful in some cases:\r
-It will toggle all order columns and should thus give you the exact\r
-opposite order.\r
-\r
-The following is a simple example of single-column ordering. It shows a list\r
-of sortable columns, each clickable, and an up/down arrow next to the one\r
-that is currently used to sort the table.\r
-\r
- Sort by:\r
- {% for column in table.columns %}\r
- {% if column.sortable %}\r
- <a href="?sort={{ column.name_toggled }}">{{ column }}</a>\r
- {% if column.is_ordered_straight %}<img src="down.png" />{% endif %}\r
- {% if column.is_ordered_reverse %}<img src="up.png" />{% endif %}\r
- {% endif %}\r
- {% endfor %}\r
-\r
-\r
-Error handling\r
---------------\r
-\r
-Passing incoming query string values from the request directly to the\r
-table constructor is a common thing to do. However, such data can easily\r
-be invalid, be it that a user manually modified it, or someone put up a\r
-broken link. In those cases, you usually would not want to raise an\r
-exception (nor be notified by Django's error notification mechanism) -\r
-there is nothing you could do anyway.\r
-\r
-Because of this, such errors will by default be silently ignored. For\r
-example, if one out of three columns in an "order_by" is invalid, the other\r
-two will still be applied:\r
-\r
- table.order_by = ('name', 'totallynotacolumn', '-date)\r
- assert table.order_by = ('name', '-date)\r
-\r
-This ensures that the following table will be created regardless of the\r
-string in "sort.\r
-\r
- table = MyTable(data, order_by=request.GET.get('sort'))\r
-\r
-However, if you want, you can disable this behaviour and have an exception\r
-raised instead, using:\r
-\r
- import django_tables\r
- django_tables.options.IGNORE_INVALID_OPTIONS = False\r
-\r
-\r
-Template Utilities\r
-------------------\r
-\r
-If you want the give your users the ability to interact with your table (e.g.\r
-change the ordering), you will need to create urls with the appropriate\r
-queries. To simplify that process, django-tables comes with a helpful\r
-templatetag:\r
-\r
- {% set_url_param sort="name" %} # ?sort=name\r
- {% set_url_param sort="" %} # delete "sort" param\r
-\r
-The template library can be found in 'django_modules.app.templates.tables'.\r
-If you add ''django_modules.app' to your INSTALLED_APPS setting, you will\r
-be able to do:\r
-\r
- {% load tables %}\r
-\r
-Note: The tag requires the current request to be available as ``request``\r
-in the context (usually, this means activating the Django request context\r
-processor).\r
+django-tables - a Django QuerySet renderer.\r
\r
+Documentation:\r
+ http://elsdoerfer.name/docs/django-tables/\r
--- /dev/null
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-tables.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-tables.qhc"
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+ "run these through (pdf)latex."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
--- /dev/null
+=================
+All about Columns
+=================
+
+Columns are what defines a table. Therefore, the way you configure your
+columns determines to a large extend how your table operates.
+
+``django_tables.columns`` currently defines three classes, ``Column``,
+``TextColumn`` and ``NumberColumn``. However, the two subclasses currently
+don't do anything special at all, so you can simply use the base class.
+While this will likely change in the future (e.g. when grouping is added),
+the base column class will continue to work by itself.
+
+There are no required arguments. The following is fine:
+
+.. code-block:: python
+
+ class MyTable(tables.MemoryTable):
+ c = tables.Column()
+
+It will result in a column named ``c`` in the table. You can specify the
+``name`` to override this:
+
+.. code-block:: python
+
+ c = tables.Column(name="count")
+
+The column is now called and accessed via "count", although the table will
+still use ``c`` to read it's values from the source. You can however modify
+that as well, by specifying ``data``:
+
+.. code-block:: python
+
+ c = tables.Column(name="count", data="count")
+
+For practicual purposes, ``c`` is now meaningless. While in most cases
+you will just define your column using the name you want it to have, the
+above is useful when working with columns automatically generated from
+models:
+
+.. code-block:: python
+
+ class BookTable(tables.ModelTable):
+ book_name = tables.Column(name="name")
+ author = tables.Column(data="info__author__name")
+ class Meta:
+ model = Book
+
+The overwritten ``book_name`` field/column will now be exposed as the
+cleaner ``name``, and the new ``author`` column retrieves it's values from
+``Book.info.author.name``.
+
+Note: ``data`` may also be a callable which will be passed a row object.
+
+Apart from their internal name, you can define a string that will be used
+when for display via ``verbose_name``:
+
+.. code-block:: python
+
+ pubdate = tables.Column(verbose_name="Published")
+
+The verbose name will be used, for example, if you put in a template:
+
+.. code-block:: django
+
+ {{ column }}
+
+If you don't want a column to be sortable by the user:
+
+.. code-block:: python
+
+ pubdate = tables.Column(sortable=False)
+
+Sorting is also affected by ``direction``, which can be used to change the
+*default* sort direction to descending. Note that this option only indirectly
+translates to the actual direction. Normal und reverse order, the terms
+django-tables exposes, now simply mean different things.
+
+.. code-block:: python
+
+ pubdate = tables.Column(direction='desc')
+
+If you don't want to expose a column (but still require it to exist, for
+example because it should be sortable nonetheless):
+
+.. code-block:: python
+
+ pubdate = tables.Column(visible=False)
+
+The column and it's values will now be skipped when iterating through the
+table, although it can still be accessed manually.
+
+Finally, you can specify default values for your columns:
+
+.. code-block:: python
+
+ health_points = tables.Column(default=100)
+
+Note that how the default is used and when it is applied differs between
+table types.
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# django-tables documentation build configuration file, created by
+# sphinx-quickstart on Fri Mar 26 08:40:14 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'django-tables'
+copyright = u'2010, Michael Elsdörfer'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'django-tablesdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index.rst', 'django-tables.tex', u'django-tables Documentation',
+ u'Michael Elsdörfer', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
--- /dev/null
+===============
+How to do stuff
+===============
+
+This section will explain some specific features in more detail.
+
+.. toctree::
+ :maxdepth: 1
+
+ ordering
+ pagination
\ No newline at end of file
--- /dev/null
+=================
+Sorting the table
+=================
+
+``django-tables`` allows you to specify which column the user can sort,
+and will validate and resolve an incoming query string value the the
+correct ordering.
+
+It will also help you rendering the correct links to change the sort
+order in your template.
+
+
+Specify which columns are sortable
+----------------------------------
+
+Tables can take a ``sortable`` option through an inner ``Meta``, the same
+concept as known from forms and models in Django:
+
+.. code-block:: python
+
+ class MyTable(tables.MemoryTable):
+ class Meta:
+ sortable = True
+
+This will be the default value for all columns, and it defaults to ``True``.
+You can override the table default for each individual column:
+
+.. code-block:: python
+
+ class MyTable(tables.MemoryTable):
+ foo = tables.Column(sortable=False)
+ class Meta:
+ sortable = True
+
+
+Setting the table ordering
+--------------------------
+
+Your table both takes a ``order_by`` argument in it's constructor, and you
+can change the order by assigning to the respective attribute:
+
+.. code-block:: python
+
+ table = MyTable(order_by='-foo')
+ table.order_by = 'foo'
+
+You can see that the value expected is pretty much what is used by the
+Django database API: An iterable of column names, optionally using a hyphen
+as a prefix to indicate reverse order. However, you may also pass a
+comma-separated string:
+
+.. code-block:: python
+
+ table = MyTable(order_by='column1,-column2')
+
+When you set ``order_by``, the value is parsed right away, and subsequent
+reads will give you the normalized value:
+
+.. code-block:: python
+
+ >>> table.order_by = ='column1,-column2'
+ >>> table.order_by
+ ('column1', '-column2')
+
+Note: Random ordering is currently not supported.
+
+
+Error handling
+~~~~~~~~~~~~~~
+
+Passing incoming query string values from the request directly to the
+table constructor is a common thing to do. However, such data can easily
+contain invalid column names, be it that a user manually modified it,
+or someone put up a broken link. In those cases, you usually would not want
+to raise an exception (nor be notified by Django's error notification
+mechanism) - there is nothing you could do anyway.
+
+Because of this, such errors will by default be silently ignored. For
+example, if one out of three columns in an "order_by" is invalid, the other
+two will still be applied:
+
+.. code-block:: python
+
+ >>> table.order_by = ('name', 'totallynotacolumn', '-date)
+ >>> table.order_by
+ ('name', '-date)
+
+This ensures that the following table will be created regardless of the
+value in ``sort``:
+
+.. code-block:: python
+
+ table = MyTable(data, order_by=request.GET.get('sort'))
+
+However, if you want, you can disable this behaviour and have an exception
+raised instead, using:
+
+.. code-block:: python
+
+ import django_tables
+ django_tables.options.IGNORE_INVALID_OPTIONS = False
+
+
+Interacting with order
+----------------------
+
+Letting the user change the order of a table is a common scenario. With
+respect to Django, this means adding links to your table output that will
+send off the appropriate arguments to the server. ``django-tables``
+attempts to help with you that.
+
+A bound column, that is a column accessed through a table instance, provides
+the following attributes:
+
+- ``name_reversed`` will simply return the column name prefixed with a
+ hyphen; this is useful in templates, where string concatenation can
+ at times be difficult.
+
+- ``name_toggled`` checks the tables current order, and will then
+ return the column either prefixed with an hyphen (for reverse ordering)
+ or without, giving you the exact opposite order. If the column is
+ currently not ordered, it will start off in non-reversed order.
+
+It is easy to be confused about the difference between the ``reverse`` and
+``toggle`` terminology. ``django-tables`` tries to put a normal/reverse-order
+abstraction on top of "ascending/descending", where as normal order could
+potentially mean either ascending or descending, depending on the column.
+
+Something you commonly see is a table that indicates which column it is
+currently ordered by through little arrows. To implement this, you will
+find useful:
+
+- ``is_ordered``: Returns ``True`` if the column is in the current
+ ``order_by``, regardless of the polarity.
+
+- ``is_ordered_reverse``, ``is_ordered_straight``: Returns ``True`` if the
+ column is ordered in reverse or non-reverse, respectively, otherwise
+ ``False``.
+
+The above is usually enough for most simple cases, where tables are only
+ordered by a single column. For scenarios in which multi-column order is
+used, additional attributes are available:
+
+- ``order_by``: Return the current order, but with the current column
+ set to normal ordering. If the current column is not already part of
+ the order, it is appended. Any existing columns in the order are
+ maintained as-is.
+
+- ``order_by_reversed``, ``order_by_toggled``: Similarly, return the
+ table's current ``order_by`` with the column set to reversed or toggled,
+ respectively. Again, it is appended if not already ordered.
+
+Additionally, ``table.order_by.toggle()`` may also be useful in some cases:
+It will toggle all order columns and should thus give you the exact
+opposite order.
+
+The following is a simple example of single-column ordering. It shows a list
+of sortable columns, each clickable, and an up/down arrow next to the one
+that is currently used to sort the table.
+
+.. code-block:: django
+
+ Sort by:
+ {% for column in table.columns %}
+ {% if column.sortable %}
+ <a href="?sort={{ column.name_toggled }}">{{ column }}</a>
+ {% if column.is_ordered_straight %}<img src="down.png" />{% endif %}
+ {% if column.is_ordered_reverse %}<img src="up.png" />{% endif %}
+ {% endif %}
+ {% endfor %}
--- /dev/null
+----------
+Pagination
+----------
+
+If your table has a large number of rows, you probably want to paginate
+the output. There are two distinct approaches.
+
+First, you can just paginate over ``rows`` as you would do with any other
+data:
+
+.. code-block:: python
+
+ table = MyTable(queryset)
+ paginator = Paginator(table.rows, 10)
+ page = paginator.page(1)
+
+You're not necessarily restricted to Django's own paginator (or subclasses) -
+any paginator should work with this approach, so long it only requires
+``rows`` to implement ``len()``, slicing, and, in the case of a
+``ModelTable``, a ``count()`` method. The latter means that the
+``QuerySetPaginator`` also works as expected.
+
+Alternatively, you may use the ``paginate`` feature:
+
+.. code-block:: python
+
+ table = MyTable(queryset)
+ table.paginate(Paginator, 10, page=1, orphans=2)
+ for row in table.rows.page():
+ pass
+ table.paginator # new attributes
+ table.page
+
+The table will automatically create an instance of ``Paginator``,
+passing it's own data as the first argument and additionally any arguments
+you have specified, except for ``page``. You may use any paginator, as long
+as it follows the Django protocol:
+
+* Take data as first argument.
+* Support a page() method returning an object with an ``object_list``
+ attribute, exposing the paginated data.
+
+Note that due to the abstraction layer that ``django-tables`` represents, it
+is not necessary to use Django's ``QuerySetPaginator`` with model tables.
+Since the table knows that it holds a queryset, it will automatically choose
+to use count() to determine the data length (which is exactly what
+``QuerySetPaginator`` would do).
\ No newline at end of file
--- /dev/null
+==========================================
+django-tables - A Django Queryset renderer
+==========================================
+
+
+``django-tables`` wants to help you present data while allowing your user
+to apply common tabular transformations on it.
+
+Currently, this mostly mostly means "sorting", i.e. parsing a query string
+coming from the browser (while supporting multiple sort fields, restricting
+the fields that may be sorted, exposing fields under different names) and
+generating the proper links to allow the user to change the sort order.
+
+In the future, filtering and grouping will hopefully be added.
+
+
+A simple example
+----------------
+
+The API looks similar to that of Django's ``ModelForms``:
+
+.. code-block:: python
+
+ import django_tables as tables
+
+ class CountryTable(tables.MemoryTable):
+ name = tables.Column(verbose_name="Country Name")
+ population = tables.Column(sortable=False, visible=False)
+ time_zone = tables.Column(name="tz", default="UTC+1")
+
+Instead of fields, you declare a column for every piece of data you want
+to expose to the user.
+
+To use the table, create an instance:
+
+.. code-block:: python
+
+ countries = CountryTable([{'name': 'Germany', population: 80},
+ {'name': 'France', population: 64}])
+
+Decide how the table should be sorted:
+
+.. code-block:: python
+
+ countries.order_by = ('name',)
+ assert [row.name for row in countries.row] == ['France', 'Germany']
+
+ countries.order_by = ('-population',)
+ assert [row.name for row in countries.row] == ['Germany', 'France']
+
+If you pass the table object along into a template, you can do:
+
+.. code-block:: django
+
+ {% for column in countries.columns %}
+ {{ column }}
+ {% endfor %}
+
+Which will give you:
+
+.. code-block:: django
+
+ Country Name
+ Timezone
+
+Note that ``population`` is skipped (as it has ``visible=False``), that the
+declared verbose name for the ``name`` column is used, and that ``time_zone``
+is converted into a more beautiful string for output automatically.
+
+
+Common Workflow
+~~~~~~~~~~~~~~~
+
+Usually, you are going to use a table like this. Assuming ``CountryTable``
+is defined as above, your view will create an instance and pass it to the
+template:
+
+.. code-block:: python
+
+ def list_countries(request):
+ data = ...
+ countries = CountryTable(data, order_by=request.GET.get('sort'))
+ return render_to_response('list.html', {'table': countries})
+
+Note that we are giving the incoming ``sort`` query string value directly to
+the table, asking for a sort. All invalid column names will (by default) be
+ignored. In this example, only ``name`` and ``tz`` are allowed, since:
+
+ * ``population`` has ``sortable=False``
+ * ``time_zone`` has it's name overwritten with ``tz``.
+
+Then, in the ``list.html`` template, write:
+
+.. code-block:: django
+
+ <table>
+ <tr>
+ {% for column in table.columns %}
+ <th><a href="?sort={{ column.name_toggled }}">{{ column }}</a></th>
+ {% endfor %}
+ </tr>
+ {% for row in table.rows %}
+ <tr>
+ {% for value in row %}
+ <td>{{ value }}</td>
+ {% endfor %}
+ </tr>
+ {% endfor %}
+ </table>
+
+This will output the data as an HTML table. Note how the table is now fully
+sortable, since our link passes along the column name via the querystring,
+which in turn will be used by the server for ordering. ``order_by`` accepts
+comma-separated strings as input, and "{{ column.name_toggled }}" will be
+rendered as a such a string.
+
+Instead of the iterator, you can alos use your knowledge of the table
+structure to access columns directly:
+
+.. code-block:: django
+
+ {% if table.columns.tz.visible %}
+ {{ table.columns.tz }}
+ {% endfor %}
+
+
+In Detail
+=========
+
+.. toctree::
+ :maxdepth: 2
+
+ installation
+ types/index
+ features/index
+ columns
+ templates
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
--- /dev/null
+------------
+Installation
+------------
+
+Adding ``django-tables`` to your ``INSTALLED_APPS`` setting is optional.
+It'll get you the ability to load some template utilities via
+``{% load tables %}``, but apart from that,
+``import django_tables as tables`` should get you going.
+
+
+Running the test suite
+----------------------
+
+The test suite uses nose:
+ http://somethingaboutorange.com/mrl/projects/nose/
\ No newline at end of file
--- /dev/null
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+set SPHINXBUILD=sphinx-build
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-tables.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-tables.ghc
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+:end
--- /dev/null
+================================
+Rendering the table in templates
+================================
+
+
+A table instance bound to data has two attributes ``columns`` and ``rows``,
+which can be iterate over:
+
+.. code-block:: django
+
+ <table>
+ <tr>
+ {% for column in table.columns %}
+ <th><a href="?sort={{ column.name_toggled }}">{{ column }}</a></th>
+ {% endfor %}
+ </tr>
+ {% for row in table.rows %}
+ <tr>
+ {% for value in row %}
+ <td>{{ value }}</td>
+ {% endfor %}
+ </tr>
+ {% endfor %}
+ </table>
+
+For the attributes available on a bound column, see :doc:`features/index`,
+depending on what you want to accomplish.
+
+
+The table.columns container
+---------------------------
+
+While you can iterate through ``columns`` and get all the currently visible
+columns, it further provides features that go beyond a simple iterator.
+
+You can access all columns, regardless of their visibility, through
+``columns.all``.
+
+``columns.sortable`` is a handy shortcut that exposes all columns which's
+``sortable`` attribute is True. This can be very useful in templates, when
+doing {% if column.sortable %} can conflict with {{ forloop.last }}.
+
+
+Template Utilities
+------------------
+
+If you want the give your users the ability to interact with your table (e.g.
+change the ordering), you will need to create urls with the appropriate
+queries. To simplify that process, django-tables comes with a helpful
+templatetag:
+
+.. code-block:: django
+
+ {% set_url_param sort="name" %} # ?sort=name
+ {% set_url_param sort="" %} # delete "sort" param
+
+The template library can be found in 'django_modules.app.templates.tables'.
+If you add ''django_modules.app' to your ``INSTALLED_APPS`` setting, you
+will be able to do:
+
+.. code-block:: django
+
+ {% load tables %}
+
+Note: The tag requires the current request to be available as ``request``
+in the context (usually, this means activating the Django request context
+processor).
--- /dev/null
+===========
+Table types
+===========
+
+Different types of tables are available:
+
+.. toctree::
+ :maxdepth: 1
+
+ MemoryTable - uses dicts as the data source <memory>
+ ModelTable - wraps around a Django Model <models>
+ SqlTable - is based on a raw SQL query <sql>
\ No newline at end of file
--- /dev/null
+-----------
+MemoryTable
+-----------
+
+This table expects an iterable of ``dict`` (or compatible) objects as the
+data source. Values found in the data that are not associated with a column
+are ignored, missing values are replaced by the column default or ``None``.
+
+Sorting is done in memory, in pure Python.
+
+Dynamic Data
+~~~~~~~~~~~~
+
+If any value in the source data is a callable, it will be passed it's own
+row instance and is expected to return the actual value for this particular
+table cell.
+
+Similarily, the colunn default value may also be callable that will take
+the row instance as an argument (representing the row that the default is
+needed for).
\ No newline at end of file
--- /dev/null
+----------
+ModelTable
+----------
+
+This table type is based on a Django model. It will use the Model's data,
+and, like a ``ModelForm``, can automatically generate it's columns based
+on the mode fields.
+
+.. code-block:: python
+
+ class CountryTable(tables.ModelTable):
+ id = tables.Column(sortable=False, visible=False)
+ class Meta:
+ model = Country
+ exclude = ['clicks']
+
+In this example, the table will have one column for each model field,
+with the exception of ``clicks``, which is excluded. The column for ``id``
+is overwritten to both hide it by default and deny it sort capability.
+
+When instantiating a ``ModelTable``, you usually pass it a queryset to
+provide the table data:
+
+.. code-block:: python
+
+ qs = Country.objects.filter(continent="europe")
+ countries = CountryTable(qs)
+
+However, you can also just do:
+
+.. code-block:: python
+
+ countries = CountryTable()
+
+and all rows exposed by the default manager of the model the table is based
+on will be used.
+
+If you are using model inheritance, then the following also works:
+
+.. code-block:: python
+
+ countries = CountryTable(CountrySubclass)
+
+Note that while you can pass any model, it really only makes sense if the
+model also provides fields for the columns you have defined.
+
+If you just want to use a ``ModelTable``, but without auto-generated
+columns, you do not have to list all model fields in the ``exclude``
+``Meta`` option. Instead, simply don't specify a model.
+
+
+Custom Columns
+~~~~~~~~~~~~~~
+
+You an add custom columns to your ModelTable that are not based on actual
+model fields:
+
+.. code-block:: python
+
+ class CountryTable(tables.ModelTable):
+ custom = tables.Column(default="foo")
+ class Meta:
+ model = Country
+
+Just make sure your model objects do provide an attribute with that name.
+Functions are also supported, so ``Country.custom`` could be a callable.
+
+
+Spanning relationships
+~~~~~~~~~~~~~~~~~~~~~~
+
+Let's assume you have a ``Country`` model, with a ``ForeignKey`` ``capital``
+pointing to the ``City`` model. While displaying a list of countries,
+you might want want to link to the capital's geographic location, which is
+stored in ``City.geo`` as a ``(lat, long)`` tuple, on, say, a Google Map.
+
+``ModelTable`` supports the relationship spanning syntax of Django's
+database API:
+
+.. code-block:: python
+
+ class CountryTable(tables.ModelTable):
+ city__geo = tables.Column(name="geo")
+
+This will add a column named "geo", based on the field by the same name
+from the "city" relationship. Note that the name used to define the column
+is what will be used to access the data, while the name-overwrite passed to
+the column constructor just defines a prettier name for us to work with.
+This is to be consistent with auto-generated columns based on model fields,
+where the field/column name naturally equals the source name.
+
+However, to make table defintions more visually appealing and easier to
+read, an alternative syntax is supported: setting the column ``data``
+property to the appropriate string.
+
+.. code-block:: python
+
+ class CountryTable(tables.ModelTable):
+ geo = tables.Column(data='city__geo')
+
+Note that you don't need to define a relationship's fields as separate
+columns if you already have a column for the relationship itself, i.e.:
+
+.. code-block:: python
+
+ class CountryTable(tables.ModelTable):
+ city = tables.Column()
+
+ for country in countries.rows:
+ print country.city.id
+ print country.city.geo
+ print country.city.founder.name
+
+
+``ModelTable`` Specialties
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``ModelTable`` currently has some restrictions with respect to ordering:
+
+* Custom columns not based on a model field do not support ordering,
+ regardless of the ``sortable`` property (it is ignored).
+
+* A ``ModelTable`` column's ``default`` or ``data`` value does not affect
+ ordering. This differs from the non-model table behaviour.
+
+If a column is mapped to a method on the model, that method will be called
+without arguments. This behavior differs from memory tables, where a
+row object will be passed.
+
+If you are using callables (e.g. for the ``default`` or ``data`` column
+options), they will generally be run when a row is accessed, and
+possible repeatedly when accessed more than once. This behavior differs from
+memory tables, where they would be called once, when the table is
+generated.
\ No newline at end of file
--- /dev/null
+--------
+SqlTable
+--------
+
+This table is backed by an SQL query that you specified. It'll help you
+ensure that pagination and sorting options are properly reflected in the
+query.
+
+**Currently not implemented yet.**
\ No newline at end of file