[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