[Python-modules-commits] [django-tables] 01/07: New upstream version 1.14.2

Brian May bam at debian.org
Mon Oct 30 21:30:35 UTC 2017


This is an automated email from the git hooks/post-receive script.

bam pushed a commit to branch debian/master
in repository django-tables.

commit 5423768ebaa22db27373ffe3ea77271d4591ff7a
Author: Brian May <bam at debian.org>
Date:   Tue Oct 31 08:01:42 2017 +1100

    New upstream version 1.14.2
---
 .isort.cfg                                     |   2 +-
 CHANGELOG.md                                   |  27 ++++++++--
 django_tables2/__init__.py                     |   2 +-
 django_tables2/columns/manytomanycolumn.py     |  16 +++++-
 django_tables2/columns/templatecolumn.py       |  23 ++++++--
 django_tables2/export/views.py                 |   6 ++-
 django_tables2/locale/hu/LC_MESSAGES/django.mo | Bin 0 -> 767 bytes
 django_tables2/locale/hu/LC_MESSAGES/django.po |  46 ++++++++++++++++
 django_tables2/rows.py                         |  27 ++++------
 django_tables2/tables.py                       |  20 +++++--
 django_tables2/utils.py                        |   2 +-
 django_tables2/views.py                        |  10 +++-
 docs/pages/api-reference.rst                   |  46 ++++++++++------
 docs/pages/custom-data.rst                     |   4 +-
 docs/pages/export.rst                          |  53 +++++++++++++++++-
 docs/pages/faq.rst                             |  28 +---------
 docs/pages/filtering.rst                       |  21 +++++++-
 docs/pages/ordering.rst                        |  27 ++++++++++
 docs/requirements.txt                          |   1 -
 example/app/data.py                            |  46 ++++++++++++++++
 example/app/migrations/0001_initial.py         |  44 +++++++++++++++
 example/app/migrations/__init__.py             |   0
 example/app/views.py                           |  12 +++--
 example/requirements.pip                       |   1 +
 requirements/common.pip                        |   2 +-
 requirements/django-dev.pip                    |   3 +-
 setup.py                                       |   1 +
 tests/columns/test_manytomanycolumn.py         |  50 +++++++++++++++--
 tests/columns/test_templatecolumn.py           |  32 +++++++++++
 tests/export/test_export.py                    |  71 +++++++++++++++++++++++--
 tests/test_core.py                             |   5 ++
 tests/test_data.py                             |  12 ++---
 tests/test_faq.py                              |  28 +---------
 tests/test_pinned_rows.py                      |   4 +-
 tests/test_rows.py                             |   8 +--
 tox.ini                                        |  10 ++--
 36 files changed, 549 insertions(+), 141 deletions(-)

diff --git a/.isort.cfg b/.isort.cfg
index abec776..04f7698 100644
--- a/.isort.cfg
+++ b/.isort.cfg
@@ -2,4 +2,4 @@
 line_length = 120
 known_third_party=django,django_filter,pytest,fudge,lxml
 known_first_party=django_tables2
-skip=migrations/*.py
+skip=migrations
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f66c223..b4884eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,27 @@
 # Change log
 
-## master (unreleased)
+## 1.14.2 (2017-10-30)
+ - Added a `row_counter` variable to the template context in `TemplateColumn` (fixes [#448](https://github.com/jieter/django-tables2/issues/488))
+
+## 1.14.1 (2017-10-30)
+ - Do not fail if `orderable=False` is passed to `ManyToManyColumn()`
+
+## 1.14.0 (2017-10-30)
+ - Added `separator` argument to `ManyToManyColumn`.
+ - Allow `mark_safe()`'d strings from `ManyToManyColumn.tranform()`
+ - Disabled ordering on `ManyToManyColumns` by default.
+
+## 1.13.0 (2017-10-17)
+ - Made positional `data` argument to the table `__init__()` a keyword argument to make inheritance easier. Will raise a `TypeError` if ommitted.
+
+## 1.12.0 (2017-10-10)
+ - Allow export filename customization [#484](https://github.com/bradleyayers/django-tables2/pull/484) by [@federicobond](https://github.com/federicobond)
+ - Fixed a bug where template columns were not rendered for pinned rows ([#483](https://github.com/bradleyayers/django-tables2/pull/483) by [@khirstinova](https://github.com/khirstinova), fixes [#482](https://github.com/bradleyayers/django-tables2/issues/482))
+
+## 1.11.0 (2017-09-15)
+ - Added Hungarian translation [#471](https://github.com/bradleyayers/django-tables2/pull/471) by [@hmikihth](https://github.com/hmikihth).
+ - Added TemplateColumn.value() and enhanced export docs (fixes [#470](https://github.com/bradleyayers/django-tables2/issues/470))
+ - Fixed display of pinned rows if table has no data. [#477](https://github.com/bradleyayers/django-tables2/pull/477) by [@khirstinova](https://github.com/khirstinova)
 
 ## 1.10.0 (2017-06-30)
  - Added `ManyToManyColumn` automatically added for `ManyToManyField`s.
@@ -104,15 +125,13 @@ _Full disclosure: as of april 1st, 2017, I am an employee of [Zostera](http://zo
 - table footers (#323)
 - Non-field based `LinkColumn` only renders default value if lookup fails. (#322)
 - Accept `text` parameter in `BaseLinkColumn`-based columns. (#322)
-- Pass the table instance into SingleTableMixin's get_table_pagination (#320 by [@georgema1982](https://github.com/georgema1982), fixes #319)
+- Pass the table instance into SingleTableMixin's `get_table_pagination` (#320 by [@georgema1982](https://github.com/georgema1982), fixes #319)
 - Check if the view has `paginate_by` before before trying to access it. (fixes #326)
 
 ## v1.2.0 (2016-05-02)
 - Allow custom attributes for rows (fixes #47)
 
 ## v1.1.8 (2016-05-02)
-- Pass the table instance into `SingleTableMixin.get_table_pagination()` (#320 by
-(@georgema1982)[https://github.com/georgema1982])
 - Ability to change the body of the `<a>`-tag, by passing `text` kwarg to the columns inheriting from BaseLinkColumn (#318 by [@desecho](https://github.com/desecho), #322)
 - Non-field based LinkColumn only renders default value if lookup fails and text is not set. (#322, fixes #257)
 
diff --git a/django_tables2/__init__.py b/django_tables2/__init__.py
index 841365c..4331584 100644
--- a/django_tables2/__init__.py
+++ b/django_tables2/__init__.py
@@ -9,7 +9,7 @@ from .utils import A
 from .views import SingleTableMixin, SingleTableView, MultiTableMixin
 
 
-__version__ = '1.10.0'
+__version__ = '1.14.2'
 
 __all__ = (
     'Table', 'TableBase',
diff --git a/django_tables2/columns/manytomanycolumn.py b/django_tables2/columns/manytomanycolumn.py
index 505b05c..487d86a 100644
--- a/django_tables2/columns/manytomanycolumn.py
+++ b/django_tables2/columns/manytomanycolumn.py
@@ -3,6 +3,7 @@ from __future__ import absolute_import, unicode_literals
 
 from django.db import models
 from django.utils.encoding import force_text
+from django.utils.html import conditional_escape, mark_safe
 
 from django_tables2.templatetags.django_tables2 import title
 
@@ -14,6 +15,8 @@ class ManyToManyColumn(Column):
     '''
     Display the list of objects from a `ManyRelatedManager`
 
+    Ordering is disabled for this column.
+
     Arguments:
         transform: callable to transform each item to text, it gets an item as argument
             and must return a string-like representation of the item.
@@ -21,6 +24,7 @@ class ManyToManyColumn(Column):
         filter: callable to filter, limit or order the QuerySet, it gets the
             `ManyRelatedManager` as first argument and must return.
             By default, it returns `all()``
+        separator: separator string to join the items with. default: ', '
 
     For example, when displaying a list of friends with their full name::
 
@@ -40,11 +44,14 @@ class ManyToManyColumn(Column):
             friends = tables.ManyToManyColumn(transform=lamda user: u.name)
 
     '''
-    def __init__(self, transform=None, filter=None, *args, **kwargs):
+    def __init__(self, transform=None, filter=None, separator=', ', *args, **kwargs):
         if transform is not None:
             self.transform = transform
         if filter is not None:
             self.filter = filter
+        self.separator = separator
+
+        kwargs.setdefault('orderable', False)
 
         super(ManyToManyColumn, self).__init__(*args, **kwargs)
 
@@ -62,10 +69,15 @@ class ManyToManyColumn(Column):
         return qs.all()
 
     def render(self, value):
+        # if value is None or not value.exists():
         if not value.exists():
             return '-'
 
-        return ', '.join(map(self.transform, self.filter(value)))
+        return mark_safe(
+            conditional_escape(self.separator).join(
+                map(conditional_escape, map(self.transform, self.filter(value)))
+            )
+        )
 
     @classmethod
     def from_field(cls, field):
diff --git a/django_tables2/columns/templatecolumn.py b/django_tables2/columns/templatecolumn.py
index 69b12fb..1a2d193 100644
--- a/django_tables2/columns/templatecolumn.py
+++ b/django_tables2/columns/templatecolumn.py
@@ -3,6 +3,8 @@ from __future__ import absolute_import, unicode_literals
 
 from django.template import Context, Template
 from django.template.loader import get_template
+from django.utils import six
+from django.utils.html import strip_tags
 
 from .base import Column, library
 
@@ -20,9 +22,10 @@ class TemplateColumn(Column):
     A `~django.template.Template` object is created from the
     *template_code* or *template_name* and rendered with a context containing:
 
-    - *record*  -- data record for the current row
-    - *value*   -- value from `record` that corresponds to the current column
-    - *default* -- appropriate default value to use as fallback
+    - *record*      -- data record for the current row
+    - *value*       -- value from `record` that corresponds to the current column
+    - *default*     -- appropriate default value to use as fallback
+    - *row_counter* -- The number of the row this cell is being rendered in.
 
     Example:
 
@@ -53,7 +56,8 @@ class TemplateColumn(Column):
             'default': bound_column.default,
             'column': bound_column,
             'record': record,
-            'value': value
+            'value': value,
+            'row_counter': kwargs['bound_row'].row_counter
         })
 
         try:
@@ -63,3 +67,14 @@ class TemplateColumn(Column):
                 return get_template(self.template_name).render(context.flatten())
         finally:
             context.pop()
+
+    def value(self, **kwargs):
+        '''
+        The value returned from a call to `value()` on a `TemplateColumn` is
+        the rendered tamplate with `django.utils.html.strip_tags` applied.
+        '''
+        html = super(TemplateColumn, self).value(**kwargs)
+        if isinstance(html, six.string_types):
+            return strip_tags(html)
+        else:
+            return html
diff --git a/django_tables2/export/views.py b/django_tables2/export/views.py
index 841079e..12dbb60 100644
--- a/django_tables2/export/views.py
+++ b/django_tables2/export/views.py
@@ -10,10 +10,11 @@ class ExportMixin(object):
     `ExportMixin` looks for some attributes on the class to change it's behaviour:
 
     Attributes:
+        export_name (str): is the name of file that will be exported, without extension.
         export_trigger_param (str): is the name of the GET attribute used to trigger
             the export. It's value decides the export format, refer to
             `TableExport` for a list of available formats.
-        excude_columns (iterable): column names excluded from the export.
+        exclude_columns (iterable): column names excluded from the export.
             For example, one might want to exclude columns containing buttons from
             the export. Excluding columns from the export is also possible using the
             `exclude_from_export` argument to the `.Column` constructor::
@@ -22,11 +23,12 @@ class ExportMixin(object):
                     name = tables.Column()
                     buttons = tables.TemplateColumn(exclude_from_export=True, template_name=...)
     '''
+    export_name = 'table'
     export_trigger_param = '_export'
     exclude_columns = ()
 
     def get_export_filename(self, export_format):
-        return 'table.{}'.format(export_format)
+        return '{}.{}'.format(self.export_name, export_format)
 
     def create_export(self, export_format):
         exporter = TableExport(
diff --git a/django_tables2/locale/hu/LC_MESSAGES/django.mo b/django_tables2/locale/hu/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..a62c508
Binary files /dev/null and b/django_tables2/locale/hu/LC_MESSAGES/django.mo differ
diff --git a/django_tables2/locale/hu/LC_MESSAGES/django.po b/django_tables2/locale/hu/LC_MESSAGES/django.po
new file mode 100644
index 0000000..e16d1b8
--- /dev/null
+++ b/django_tables2/locale/hu/LC_MESSAGES/django.po
@@ -0,0 +1,46 @@
+# This file is distributed under the same license as the django-tables2 package
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: django-tables2\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2016-04-19 10:22+0200\n"
+"PO-Revision-Date: 2017-08-13 14:19+0200\n"
+"Last-Translator: Miklos Horvath <hmiki at blackpantheros.eu>\n"
+"Language-Team: Hungarian <hu at li.org>\n"
+"Language: en\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.12\n"
+
+#: templates/django_tables2/bootstrap.html:35
+msgid "no results"
+msgstr "nincs eredmény"
+
+#: templates/django_tables2/bootstrap.html:55
+msgid "previous"
+msgstr "előző"
+
+#: templates/django_tables2/bootstrap.html:59
+#: templates/django_tables2/table.html:64
+#, python-format
+msgid "Page %(current)s of %(total)s"
+msgstr "Oldal: %(current)s / %(total)s"
+
+#: templates/django_tables2/bootstrap.html:64
+msgid "next"
+msgstr "következő"
+
+#: templates/django_tables2/table.html:56
+msgid "Previous"
+msgstr "Előző"
+
+#: templates/django_tables2/table.html:72
+msgid "Next"
+msgstr "Következő"
+
+#: templates/django_tables2/table.html:78
+#, python-format
+msgid "%(count)s of %(total)s"
+msgstr "%(count)s / %(total)s"
diff --git a/django_tables2/rows.py b/django_tables2/rows.py
index 823462a..949b47e 100644
--- a/django_tables2/rows.py
+++ b/django_tables2/rows.py
@@ -64,6 +64,8 @@ class BoundRow(object):
         self._record = record
         self._table = table
 
+        self.row_counter = next(table._counter)
+
     @property
     def table(self):
         '''
@@ -78,7 +80,7 @@ class BoundRow(object):
         Return:
             string: `even` for even records, `odd` otherwise.
         '''
-        return 'odd' if next(self._table._counter) % 2 else 'even'
+        return 'odd' if self.row_counter % 2 else 'even'
 
     @property
     def attrs(self):
@@ -253,22 +255,6 @@ class BoundPinnedRow(BoundRow):
         row_attrs['class'] = css_class
         return AttributeDict(row_attrs)
 
-    def _get_and_render_with(self, name, render_func, default):
-        '''
-        Get raw value from record for render in table.
-        This value using by render_func.
-
-        Arguments:
-            name: String describing a path from one object to another.
-            render_func: Only for compatibility - not used.
-
-        Return:
-            object: Raw value from record for single cell.
-        '''
-        accessor = A(name)
-        value = accessor.resolve(context=self._record, quiet=True) or default
-        return value
-
 
 class BoundRows(object):
     '''
@@ -324,7 +310,12 @@ class BoundRows(object):
             yield pinned_record
 
     def __len__(self):
-        return len(self.data)
+        length = len(self.data)
+        pinned_top = self.pinned_data.get('top')
+        pinned_bottom = self.pinned_data.get('bottom')
+        length += 0 if pinned_top is None else len(pinned_top)
+        length += 0 if pinned_bottom is None else len(pinned_bottom)
+        return length
 
     def __getitem__(self, key):
         '''
diff --git a/django_tables2/tables.py b/django_tables2/tables.py
index 569aba3..94d7cbe 100644
--- a/django_tables2/tables.py
+++ b/django_tables2/tables.py
@@ -152,6 +152,7 @@ class TableBase(object):
 
     Arguments:
         data (queryset, list of dicts): The data to display.
+            This is a required variable, a `TypeError` will be raised if it's not passed.
 
         order_by: (tuple or str): The default ordering tuple or comma separated str.
             A hyphen `-` can be used to prefix a column name to indicate
@@ -211,13 +212,18 @@ class TableBase(object):
         extra_columns (str, `.Column`): list of `(name, column)`-tuples containing
             extra columns to add to the instance.
     '''
-    def __init__(self, data, order_by=None, orderable=None, empty_text=None,
+    def __init__(self, data=None, order_by=None, orderable=None, empty_text=None,
                  exclude=None, attrs=None, row_attrs=None, pinned_row_attrs=None,
                  sequence=None, prefix=None, order_by_field=None, page_field=None,
                  per_page_field=None, template=None, default=None, request=None,
                  show_header=None, show_footer=True, extra_columns=None):
         super(TableBase, self).__init__()
 
+        # note that although data is a keyword argument, it used to be positional
+        # so it is assumed to be the first argument to this method.
+        if data is None:
+            raise TypeError('Argument data to {} is required'.format(type(self).__name__))
+
         self.exclude = exclude or self._meta.exclude
         self.sequence = sequence
         self.data = TableData.from_data(data=data, table=self)
@@ -302,6 +308,9 @@ class TableBase(object):
         '''
         Return data for top pinned rows containing data for each row.
         Iterable type like: queryset, list of dicts, list of objects.
+        Having a non-zero number of pinned rows
+        will not result in an empty resultset message being rendered,
+        even if there are no regular data rows
 
         Returns:
             `None` (default) no pinned rows at the top, iterable, data for pinned rows at the top.
@@ -323,6 +332,9 @@ class TableBase(object):
         '''
         Return data for bottom pinned rows containing data for each row.
         Iterable type like: queryset, list of dicts, list of objects.
+        Having a non-zero number of pinned rows
+        will not result in an empty resultset message being rendered,
+        even if there are no regular data rows
 
         Returns:
             `None` (default) no pinned rows at the bottom, iterable, data for pinned rows at the bottom.
@@ -420,10 +432,10 @@ class TableBase(object):
             force_text(column.header, strings_only=True)
             for column in self.columns if not excluded(column)
         ]
-        for r in self.rows:
+        for row in self.rows:
             yield [
-                force_text(r.get_cell_value(column.name), strings_only=True)
-                for column in r.table.columns if not excluded(column)
+                force_text(row.get_cell_value(column.name), strings_only=True)
+                for column in row.table.columns if not excluded(column)
             ]
 
     def has_footer(self):
diff --git a/django_tables2/utils.py b/django_tables2/utils.py
index aa2716f..4b447b3 100644
--- a/django_tables2/utils.py
+++ b/django_tables2/utils.py
@@ -344,7 +344,7 @@ class Accessor(str):
                 if current is None:
                     break
             return current
-        except:
+        except Exception:
             if not quiet:
                 raise
 
diff --git a/django_tables2/views.py b/django_tables2/views.py
index c185cac..d517d8b 100644
--- a/django_tables2/views.py
+++ b/django_tables2/views.py
@@ -79,7 +79,7 @@ class SingleTableMixin(TableMixinBase):
         sorting and pagination.
         '''
         table_class = self.get_table_class()
-        table = table_class(self.get_table_data(), **kwargs)
+        table = table_class(data=self.get_table_data(), **kwargs)
         RequestConfig(self.request, paginate=self.get_table_pagination(table)).configure(table)
         return table
 
@@ -102,6 +102,14 @@ class SingleTableMixin(TableMixinBase):
     def get_table_kwargs(self):
         '''
         Return the keyword arguments for instantiating the table.
+
+        Allows passing customized arguments to the table constructor, for example, to remove the buttons column,
+        you could define this method in your View::
+
+            def get_table_kwargs(self):
+                return {
+                    'exclude': ('buttons', )
+                }
         '''
         return {}
 
diff --git a/docs/pages/api-reference.rst b/docs/pages/api-reference.rst
index 072b0c8..38e4877 100644
--- a/docs/pages/api-reference.rst
+++ b/docs/pages/api-reference.rst
@@ -255,114 +255,126 @@ API Reference
     unlocalize (str or tuple): Specifies which fields should be unlocalized in
         the table. Read :ref:`localization-control` for more information.
 
+Columns
+-------
 
 `.Column`
----------
+~~~~~~~~~
 
 .. autoclass:: django_tables2.columns.Column
     :members: render, value, order
 
 
 `.BooleanColumn`
-----------------
+~~~~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.columns.BooleanColumn
 
 
 `.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:
 
 
 `.JSONColumn`
--------------
+~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.columns.JSONColumn
     :members:
 
 `.LinkColumn`
--------------
+~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.columns.LinkColumn
     :members:
 
 `.ManyToManyColumn`
--------------------
+~~~~~~~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.columns.ManyToManyColumn
     :members:
 
 `.RelatedLinkColumn`
---------------------
+~~~~~~~~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.columns.RelatedLinkColumn
     :members:
 
 
 `.TemplateColumn`
------------------
+~~~~~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.columns.TemplateColumn
     :members:
 
 
 `.URLColumn`
-------------
+~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.columns.URLColumn
     :members:
 
+Views and view mixins
+---------------------
+
 `.SingleTableMixin`
--------------------
+~~~~~~~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.views.SingleTableMixin
     :members:
 
 
 `.MultiTableMixin`
-------------------
+~~~~~~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.views.MultiTableMixin
     :members:
 
+`.SingleTableView`
+~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: django_tables2.views.SingleTableView
+    :members: get_table, get_table_kwargs
+
+
 `.export.TableExport`
----------------------
+~~~~~~~~~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.export.TableExport
     :members:
 
 `.export.ExportMixin`
----------------------
+~~~~~~~~~~~~~~~~~~~~~
 
 .. autoclass:: django_tables2.export.ExportMixin
     :members:
diff --git a/docs/pages/custom-data.rst b/docs/pages/custom-data.rst
index a02035b..851dd49 100644
--- a/docs/pages/custom-data.rst
+++ b/docs/pages/custom-data.rst
@@ -87,6 +87,8 @@ argument.
     a default value is rendered instead (both `.Column.render` and
     ``Table.render_FOO`` are skipped).
 
+.. _table.value_foo:
+
 `Table.value_foo` methods
 -------------------------
 
@@ -94,7 +96,7 @@ If you want to use `Table.as_values` to export your data, you might want to defi
 a method ``value_foo``, which is analogous to ``render_foo``, but used to render the
 values rather than the HTML output.
 
-Please refer to `~.Table.as_values` for an example.
+Please refer to `.Table.as_values` for an example.
 
 .. _subclassing-column:
 
diff --git a/docs/pages/export.rst b/docs/pages/export.rst
index 90a639b..04d0a34 100644
--- a/docs/pages/export.rst
+++ b/docs/pages/export.rst
@@ -25,12 +25,19 @@ Adding ability to export the table data to a class based views looks like this::
         template_name = 'django_tables2/bootstrap.html'
 
 
-Now, if you append ``_report=csv`` to the querystring, the browser will download
+Now, if you append ``_export=csv`` to the querystring, the browser will download
 a csv file containing your data. Supported export formats are:
 
     csv, json, latex, ods, tsv, xls, xlsx, yml
 
-If you must use a function view, you might use someting like this::
+To customize the name of the query parameter add an ``export_trigger_param``
+attribute to your class.
+
+By default, the file will be named ``table.ext``, where ``ext`` is the requested
+export format extension. To customize this name, add a ``export_name`` attribute
+to your class. The correct extension will be appended automatically to this value.
+
+If you must use a function view, you might use something like this::
 
     from django_tables2.config import RequestConfig
     from django_tables2.export.export import TableExport
@@ -52,6 +59,23 @@ If you must use a function view, you might use someting like this::
             'table': table
         })
 
+What exacly is exported?
+------------------------
+
+The export views use the `.Table.as_values()` method to get the data from the table.
+Because we often use HTML in our table cells, we need to specify something else for the
+export to make sense.
+
+If you use :ref:`table.render_foo`-methods to customize the output for a column,
+you should define a :ref:`table.value_foo`-method, returning the value you want
+to be exported.
+
+If you are creating your own custom columns, you should know that each column
+defines a `value()` method, which is used in `Table.as_values()`.
+By default, it just calls the `render()` method on that column.
+If your custom column produces HTML, you should override this method and return
+the actual value.
+
 
 Excluding columns
 -----------------
@@ -77,3 +101,28 @@ If you use the ``~.ExportMixin``, add an ``exclude_columns`` attribute to your c
         model = Person
         template_name = 'django_tables2/bootstrap.html'
         exclude_column = ('buttons', )
+
+
+Generating export urls
+----------------------
+
+You can use the ``querystring`` template tag included with django_tables2
+to render a link to export the data as ``csv``::
+
+    {% querystring '_export'='csv' %}
+
+This will make sure any other query string parameters will be preserved, for example
+in combination when filtering table items.
+
+If you want to render more than one button, you could use something like this::
+
+    {% for format in table.export_formats %}
+        <a href="{% querystring '_export'=format %}">
+            download  <code>.{{ format }}</code>
+        </a>
+    {% endfor %}
+
+.. note::
+
+    This example assumes you define a list of possible
+    export formats on your table instance in attribute ``export_formats``
diff --git a/docs/pages/faq.rst b/docs/pages/faq.rst
index 07103de..e0723df 100644
--- a/docs/pages/faq.rst
+++ b/docs/pages/faq.rst
@@ -44,34 +44,8 @@ How to create a row counter?
 You can use `itertools.counter` to add row count to a table. Note that in a
 paginated table, every page's counter will start at zero.
 
-Use a `render_counter()`-method::
-
-    import itertools
-
     class CountryTable(tables.Table):
-        counter = tables.Column(empty_values=(), orderable=False)
-
-        def render_counter(self):
-            self.row_counter = getattr(self, 'row_counter', itertools.count())
-            return next(self.row_counter)
-
-
-
-Or create a specialized column::
-
-    import itertools
-
-    class CounterColumn(tables.Column):
-        def __init__(self, *args, **kwargs):
-            self.counter = itertools.count()
-            kwargs.update({
-                'empty_values': (),
-                'orderable': False
-            })
-            super(CounterColumn, self).__init__(*args, **kwargs)
-
-        def render(self):
-            return next(self.counter)
+        counter = tables.TemplateColumn('{{ row_counter }}')
 
 
 How to add a footer containing a column total?
diff --git a/docs/pages/filtering.rst b/docs/pages/filtering.rst
index f33a026..3e6e87a 100644
--- a/docs/pages/filtering.rst
+++ b/docs/pages/filtering.rst
@@ -5,15 +5,32 @@ When presenting a large amount of data, filtering is often a necessity.
 Fortunately, filtering the data in your django-tables2 table is simple with
 `django-filter <https://pypi.python.org/pypi/django-filter>`_.
 
-The basis of a filterted table is a `SingleTableView` combined with a
+The basis of a filtered table is a `SingleTableMixin` combined with a
 `FilterView` from django-filter::
 
     from django_filters.views import FilterView
+    from django_tables2.views import SingleTableMixin
 
 
-    class FilteredPersonListView(FilterView, SingleTableView):
+    class FilteredPersonListView(SingleTableMixin, FilterView):
         table_class = PersonTable
         model = Person
         template_name = 'template.html'
 
         filterset_class = PersonFilter
+
+
+The filterset is added to the template context in a ``filter`` variable by
+default. A basic template rendering the filter (using django-bootstrap3) and
+table looks like this::
+
+    {% load render_table from django_tables2 %}
+    {% load bootstrap3 %}
+
+    {% if filter %}
+        <form action="" method="get" class="form form-inline">
+            {% bootstrap_form filter.form layout='inline' %}
+            {% bootstrap_button 'filter' %}
+        </form>
+    {% endif %}
+    {% render_table table 'django_tables2/bootstrap.html' %}
diff --git a/docs/pages/ordering.rst b/docs/pages/ordering.rst
index 6c261d5..2b0d192 100644
--- a/docs/pages/ordering.rst
+++ b/docs/pages/ordering.rst
@@ -119,3 +119,30 @@ This can be achieved like this::
                 amount=F('shirts') + F('pants')
             ).order_by(('-' if is_descending else '') + 'amount')
             return (queryset, True)
+
+
+Using :meth:`Column.order` on custom columns
+--------------------------------------------
+
+If you created a custom column, which also requires custom ordering like
+explained above, you can add the body of your ``order_foo`` method to the
+order method on your custom column, to allow easier reuse.
+
+For example, the `PersonTable` from above could also be defined like this::
+
+    class ClothingColumn(tables.Column):
+        def render(self, record):
+            return str(record.shirts + record.pants)
+
+        def order(self, queryset, is_descending):
+            queryset = queryset.annotate(
+                amount=F('shirts') + F('pants')
+            ).order_by(('-' if is_descending else '') + 'amount')
+            return (queryset, True)
+
+
+    class PersonTable(tables.Table):
+        clothing = ClothingColumn()
+
+        class Meta:
+            model = Person
diff --git a/docs/requirements.txt b/docs/requirements.txt
index d7767d1..8ef08c7 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -2,4 +2,3 @@
 Sphinx==1.6.2
 sphinx_rtd_theme
 recommonmark
-tablib
diff --git a/example/app/data.py b/example/app/data.py
new file mode 100644
index 0000000..ffd72a5
--- /dev/null
+++ b/example/app/data.py
@@ -0,0 +1,46 @@
+COUNTRIES = '''Aruba;104822
+Afghanistan;34656032
+Angola;28813463
+Albania;2876101
+Andorra;77281
+Arab World;406452690
+United Arab Emirates;9269612
+Argentina;43847430
+Armenia;2924816
+American Samoa;55599
+Antigua and Barbuda;100963
+Australia;24127159
+Austria;8747358
+Azerbaijan;9762274
+Burundi;10524117
+Belgium;11348159
+Benin;10872298
+Burkina Faso;18646433
+Bangladesh;162951560
+Bulgaria;7127822
+Bahrain;1425171
+Bahamas, The;391232
+Bosnia and Herzegovina;3516816
+Belarus;9507120
+Belize;366954
+Bermuda;65331
+Bolivia;10887882
+Brazil;207652865
+Barbados;284996
+Brunei Darussalam;423196
+Bhutan;797765
+Botswana;2250260
+Central African Republic;4594621
+Canada;36286425
+Switzerland;8372098
+Channel Islands;164541
+Chile;17909754
+China;1378665000
+Cote d'Ivoire;23695919
+Cameroon;23439189
+Congo, Dem. Rep.;78736153
+Congo, Rep.;5125821
+Colombia;48653419
+Comoros;795601
+Cabo Verde;539560
+'''
diff --git a/example/app/migrations/0001_initial.py b/example/app/migrations/0001_initial.py
new file mode 100644
index 0000000..5952cfe
--- /dev/null
+++ b/example/app/migrations/0001_initial.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.5 on 2017-09-22 13:23
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Country',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=100)),
+                ('population', models.PositiveIntegerField(verbose_name='population')),
+                ('tz', models.CharField(max_length=50)),
+                ('visits', models.PositiveIntegerField()),
+                ('commonwealth', models.NullBooleanField()),
+                ('flag', models.FileField(upload_to='country/flags/')),
+            ],
+            options={
+                'verbose_name_plural': 'countries',
+            },
+        ),
+        migrations.CreateModel(
+            name='Person',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=200, verbose_name='full name')),
+                ('friendly', models.BooleanField(default=True)),
+                ('country', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='app.Country')),
+            ],
+            options={
+                'verbose_name_plural': 'people',
+            },
+        ),
+    ]
diff --git a/example/app/migrations/__init__.py b/example/app/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/example/app/views.py b/example/app/views.py
index 967b7dc..961f84a 100644
--- a/example/app/views.py
+++ b/example/app/views.py
@@ -5,10 +5,11 @@ from django.shortcuts import render
 from django.utils.lorem_ipsum import words
 from django.views.generic.base import TemplateView
 
-from django_filters.views import FilterView
-from django_tables2 import MultiTableMixin, RequestConfig, SingleTableView
+from django_filters.views import FilterMixin, FilterView
+from django_tables2 import MultiTableMixin, RequestConfig, SingleTableMixin, SingleTableView
 from django_tables2.export.views import ExportMixin
 
+from .data import COUNTRIES
 from .filters import PersonFilter
 from .models import Country, Person
 from .tables import BootstrapTable, CountryTable, PersonTable, SemanticTable, ThemedCountryTable
@@ -22,6 +23,11 @@ except ImportError:
 
 def create_fake_data():
     # create some fake data to make sure we need to paginate
+    if Country.objects.all().count() < 50:
+        for country in COUNTRIES.splitlines():
+            name, population = country.split(';')
+            Country.objects.create(name=name, visits=0, population=int(population))
+
     if Person.objects.all().count() < 50:
         countries = list(Country.objects.all()) + [None]
         Person.objects.bulk_create([
@@ -130,7 +136,7 @@ def tutorial(request):
     return render(request, 'tutorial.html', {'people': Person.objects.all()})
 
 
-class FilteredPersonListView(FilterView, ExportMixin, SingleTableView):
+class FilteredPersonListView(SingleTableMixin, FilterView):
     table_class = PersonTable
     model = Person
     template_name = 'bootstrap_template.html'
... 434 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-tables.git



More information about the Python-modules-commits mailing list