`` elements.
+ ---------
+ text (str or callable): If set, this value will be used to render the text inside link instead of value.
+ The callable gets the record being rendered as argument.
+ attrs (dict): In addition to ``attrs`` keys supported by `~.Column`, the following are available:
+ - `a` -- ```` in ``
``.
@@ -247,9 +236,7 @@ def items(self):
class BoundPinnedRow(BoundRow):
- """
- Represents a *pinned* row in a table.
- """
+ """Represents a *pinned* row in a table."""
@property
def attrs(self):
@@ -258,7 +245,8 @@ def attrs(self):
Add CSS classes `pinned-row` and `odd` or `even` to `class` attribute.
Return:
- AttributeDict: Attributes for pinned rows.
+ ------
+ AttributeDict: Attributes for pinned rows.
"""
row_attrs = computed_values(self._table.pinned_row_attrs, kwargs={"record": self._record})
css_class = " ".join(
@@ -273,12 +261,14 @@ class BoundRows:
Container for spawning `.BoundRow` objects.
Arguments:
+ ---------
data: iterable of records
table: the `~.Table` in which the rows exist
pinned_data: dictionary with iterable of records for top and/or
bottom pinned rows.
Example:
+ -------
>>> pinned_data = {
... 'top': iterable, # or None value
... 'bottom': iterable, # or None value
@@ -297,10 +287,12 @@ def generator_pinned_row(self, data):
Top and bottom pinned rows generator.
Arguments:
- data: Iterable data for all records for top or bottom pinned rows.
+ ---------
+ data: Iterable data for all records for top or bottom pinned rows.
Yields:
- BoundPinnedRow: Top or bottom `BoundPinnedRow` object for single pinned record.
+ ------
+ BoundPinnedRow: Top or bottom `BoundPinnedRow` object for single pinned record.
"""
if data is not None:
if hasattr(data, "__iter__") is False:
diff --git a/django_tables2/tables.py b/django_tables2/tables.py
index f7fcded4..7fbaba97 100644
--- a/django_tables2/tables.py
+++ b/django_tables2/tables.py
@@ -111,6 +111,7 @@ class TableOptions:
variables in this class.
Arguments:
+ ---------
options (`.Table.Meta`): options for a table from `.Table.Meta`
"""
@@ -154,9 +155,7 @@ def __init__(self, options, class_name):
self.unlocalize = getattr(options, "unlocalize", ())
def _check_types(self, options, class_name):
- """
- Check class Meta attributes to prevent common mistakes.
- """
+ """Check class Meta attributes to prevent common mistakes."""
if options is None:
return
@@ -188,6 +187,7 @@ class Table(metaclass=DeclarativeColumnsMetaclass):
A representation of a table.
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.
@@ -375,15 +375,18 @@ def get_top_pinned_data(self):
Iterable type like: QuerySet, list of dicts, list of objects.
Having a non-zero number of pinned rows
will not result in an empty result set message being rendered,
- even if there are no regular data rows
+ 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.
Note:
+ ----
To show pinned row this method should be overridden.
Example:
+ -------
>>> class TableWithTopPinnedRows(Table):
... def get_top_pinned_data(self):
... return [{
@@ -396,18 +399,20 @@ def get_top_pinned_data(self):
def get_bottom_pinned_data(self):
"""
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 result set message being rendered,
- even if there are no regular data rows
+
+ Iterable type like: QuerySet, list of dicts, list of objects. Having a non-zero number of pinned rows will not
+ result in an empty result set 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.
+ -------
+ `None` (default) no pinned rows at the bottom, iterable, data for pinned rows at the bottom.
Note:
- To show pinned row this method should be overridden.
+ ----
+ To show pinned row this method should be overridden.
Example:
+ -------
>>> class TableWithBottomPinnedRows(Table):
... def get_bottom_pinned_data(self):
... return [{
@@ -419,14 +424,14 @@ def get_bottom_pinned_data(self):
def before_render(self, request):
"""
- A way to hook into the moment just before rendering the template.
+ Call before rendering the table.
Can be used to hide a column.
Arguments:
- request: contains the `WGSIRequest` instance, containing a `user` attribute if
- `.django.contrib.auth.middleware.AuthenticationMiddleware` is added to
- your `MIDDLEWARE_CLASSES`.
+ ---------
+ request: contains the `WGSIRequest` instance, containing a `user` attribute if
+ `.django.contrib.auth.middleware.AuthenticationMiddleware` is added to your `MIDDLEWARE_CLASSES`.
Example::
@@ -440,12 +445,10 @@ def before_render(self, request):
else:
self.columns.show('country')
"""
- return
+ pass
def as_html(self, request):
- """
- Render the table to an HTML table, adding `request` to the context.
- """
+ """Render the table to an HTML table, adding `request` to the context."""
# reset counter for new rendering
self._counter = count()
template = get_template(self.template_name)
@@ -457,11 +460,11 @@ def as_html(self, request):
def as_values(self, exclude_columns=None):
"""
- Return a row iterator of the data which would be shown in the table where
- the first row is the table headers.
+ Return a row iterator of the data which would be shown in the table where the first row is the table headers.
- arguments:
- exclude_columns (iterable): columns to exclude in the data iterator.
+ Arguments:
+ ---------
+ exclude_columns (iterable): columns to exclude in the data iterator.
This can be used to output the table data as CSV, excel, for example using the
`~.export.ExportMixin`.
@@ -501,10 +504,7 @@ def value_name(self, value):
]
def has_footer(self):
- """
- Returns True if any of the columns define a ``_footer`` attribute or a
- ``render_footer()`` method
- """
+ """Return True if any of the columns define a ``_footer`` attribute or a ``render_footer()`` method."""
return self.show_footer and any(column.has_footer() for column in self.columns)
@property
@@ -525,7 +525,8 @@ def order_by(self, value):
Order the rows of the table based on columns.
Arguments:
- value: iterable or comma separated string of order by aliases.
+ ---------
+ value: iterable or comma separated string of order by aliases.
"""
# collapse empty values to ()
order_by = () if not value else value
@@ -561,23 +562,22 @@ def page_field(self, value):
def paginate(self, paginator_class=Paginator, per_page=None, page=1, *args, **kwargs):
"""
- Paginates the table using a paginator and creates a ``page`` property
- containing information for the current page.
+ Paginate the table with paginator_class and add property ``page`` containing information for the current page.
Arguments:
- paginator_class (`~django.core.paginator.Paginator`): A paginator class to
- paginate the results.
+ ---------
+ paginator_class (`~django.core.paginator.Paginator`): A paginator class to paginate the results.
+ per_page (int): Number of records to display on each page.
+ page (int): Page to display.
+ *args: Positional argument passed to the paginator
+ **kwargs: Keyword argument passed to the paginator
- per_page (int): Number of records to display on each page.
- page (int): Page to display.
Extra arguments are passed to the paginator.
- Pagination exceptions (`~django.core.paginator.EmptyPage` and
- `~django.core.paginator.PageNotAnInteger`) may be raised from this
- method and should be handled by the caller.
+ Pagination exceptions (`~django.core.paginator.EmptyPage` and `~django.core.paginator.PageNotAnInteger`) may be
+ raised from this method and should be handled by the caller.
"""
-
per_page = per_page or self._meta.per_page
self.paginator = paginator_class(self.rows, per_page, *args, **kwargs)
self.page = self.paginator.page(page)
@@ -649,34 +649,30 @@ def template_name(self, value):
@property
def paginated_rows(self):
- """
- Return the rows for the current page if the table is paginated, else all rows.
- """
+ """Return the rows for the current page if the table is paginated, else all rows."""
if hasattr(self, "page"):
return self.page.object_list
return self.rows
def get_column_class_names(self, classes_set, bound_column):
"""
- Returns a set of HTML class names for cells (both ``td`` and ``th``) of a
- **bound column** in this table.
- By default this returns the column class names defined in the table's
- attributes.
- This method can be overridden to change the default behavior, for
- example to simply `return classes_set`.
+ Return a set of HTML class names for cells (both ``td`` and ``th``) of a **bound column** in this table.
+
+ By default this returns the column class names defined in the table's attributes.
+ This method can be overridden to change the default behavior, for example to simply `return classes_set`.
Arguments:
- classes_set(set of string): a set of class names to be added
- to the cell, retrieved from the column's attributes. In the case
- of a header cell (th), this also includes ordering classes.
- To set the classes for a column, see `.Column`.
- To configure ordering classes, see :ref:`ordering-class-name`
+ ---------
+ classes_set(set of string): a set of class names to be added to the cell, retrieved from the column's
+ attributes. In the case of a header cell (th), this also includes ordering classes. To set the classes
+ for a column, see `.Column`. To configure ordering classes, see :ref:`ordering-class-name`
- bound_column(`.BoundColumn`): the bound column the class names are
- determined for. Useful for accessing `bound_column.name`.
+ bound_column(`.BoundColumn`): the bound column the class names are determined for. Useful for accessing
+ `bound_column.name`.
Returns:
- A set of class names to be added to cells of this column
+ -------
+ A set of class names to be added to cells of this column
If you want to add the column names to the list of classes for a column,
override this method in your custom table::
@@ -695,18 +691,21 @@ def get_column_class_names(self, classes_set, bound_column):
def table_factory(model, table=Table, fields=None, exclude=None, localize=None):
"""
- Return Table class for given `model`, equivalent to defining a custom table class::
+ Return Table class for given `model`, equivalent to defining a custom table class.
+ Example:
+ -------
class MyTable(tables.Table):
class Meta:
model = model
Arguments:
- model (`~django.db.models.Model`): Model associated with the new table
- table (`.Table`): Base Table class used to create the new one
- fields (list of str): Fields displayed in tables
- exclude (list of str): Fields exclude in tables
- localize (list of str): Fields to localize
+ ---------
+ model (`~django.db.models.Model`): Model associated with the new table
+ table (`.Table`): Base Table class used to create the new one
+ fields (list of str): Fields displayed in tables
+ exclude (list of str): Fields exclude in tables
+ localize (list of str): Fields to localize
"""
attrs = {"model": model}
if fields is not None:
diff --git a/django_tables2/templatetags/django_tables2.py b/django_tables2/templatetags/django_tables2.py
index cfcf5276..76d5ce09 100644
--- a/django_tables2/templatetags/django_tables2.py
+++ b/django_tables2/templatetags/django_tables2.py
@@ -26,9 +26,9 @@
def token_kwargs(bits, parser):
"""
- Based on Django's `~django.template.defaulttags.token_kwargs`, but with a
- few changes:
+ Based on Django's `~django.template.defaulttags.token_kwargs`, but with a few changes.
+ Changes:
- No legacy mode.
- Both keys and values are compiled as a filter
"""
@@ -81,11 +81,12 @@ def render(self, context):
@register.tag
def querystring(parser, token):
"""
- Creates a URL (containing only the query string [including "?"]) derived
- from the current URL's query string, by updating it with the provided
- keyword arguments.
+ Compile a query string based on current query string overridden with supplied values.
- Example (imagine URL is ``/abc/?gender=male&name=Brad``)::
+ Creates a URL (containing only the query string [including "?"]) derived from the current URL's query string,
+ by updating it with the provided keyword arguments.
+
+ Example (with URL: ``/abc/?gender=male&name=Brad``)::
# {% querystring "name"="abc" "age"=15 %}
?name=abc&gender=male&age=15
@@ -119,9 +120,12 @@ def querystring(parser, token):
class RenderTableNode(Node):
"""
- parameters:
+ Node to render a table.
+
+ Parameters
+ ----------
table (~.Table): the table to render
- template (str or list): Name[s] of template to render
+ template (str or list): Name[s] of template to render.
"""
def __init__(self, table, template_name=None):
@@ -213,8 +217,7 @@ class Meta:
@register.simple_tag(takes_context=True)
def export_url(context, export_format, export_trigger_param=None):
"""
- Returns an export URL for the given file `export_format`, preserving current
- query string parameters.
+ Return an export URL for the given file `export_format`, preserving current query string parameters.
Example for a page requested with querystring ``?q=blue``::
@@ -224,7 +227,6 @@ def export_url(context, export_format, export_trigger_param=None):
?q=blue&_export=csv
"""
-
if export_trigger_param is None and "view" in context:
export_trigger_param = getattr(context["view"], "export_trigger_param", None)
@@ -243,11 +245,11 @@ def table_page_range(page, paginator):
- containing one or two '...' to skip ranges between first/last and current.
Example:
+ -------
{% for p in table.page|table_page_range:table.paginator %}
{{ p }}
{% endfor %}
"""
-
page_range = getattr(settings, "DJANGO_TABLES2_PAGE_RANGE", 10)
num_pages = paginator.num_pages
diff --git a/django_tables2/utils.py b/django_tables2/utils.py
index 789834a1..d7aff0db 100644
--- a/django_tables2/utils.py
+++ b/django_tables2/utils.py
@@ -11,7 +11,7 @@
class Sequence(list):
"""
- Represents a column sequence, e.g. ``('first_name', '...', 'last_name')``
+ Object to represent a column sequence, e.g. ``('first_name', '...', 'last_name')``.
This is used to represent `.Table.Meta.sequence` or the `.Table`
constructors's *sequence* keyword argument.
@@ -24,16 +24,19 @@ class Sequence(list):
def expand(self, columns):
"""
- Expands the ``'...'`` item in the sequence into the appropriate column
- names that should be placed there.
+ Expand the ``'...'`` item in the sequence into the appropriate column names that should be placed there.
- arguments:
- columns (list): list of column names.
- returns:
- The current instance.
+ Arguments:
+ ---------
+ columns (list): list of column names.
+
+ Returns:
+ -------
+ The current instance.
- raises:
- `ValueError` if the sequence is invalid for the columns.
+ Raises:
+ ------
+ `ValueError` if the sequence is invalid for the columns.
"""
ellipses = self.count("...")
if ellipses > 1:
@@ -83,14 +86,13 @@ def __new__(cls, value):
@property
def bare(self):
"""
- Returns:
- `.OrderBy`: the bare form.
-
- The *bare form* is the non-prefixed form. Typically the bare form is
- just the ascending form.
+ Returns
+ -------
+ `.OrderBy`: the bare form.
- Example: ``age`` is the bare form of ``-age``
+ The *bare form* is the non-prefixed form. Typically the bare form is just the ascending form.
+ Example: ``age`` is the bare form of ``-age``.
"""
return OrderBy(self[1:]) if self[:1] == "-" else self
@@ -99,8 +101,9 @@ def opposite(self):
"""
Provides the opposite of the current sorting direction.
- Returns:
- `.OrderBy`: object with an opposite sort influence.
+ Returns
+ -------
+ `.OrderBy`: object with an opposite sort influence.
Example::
@@ -113,29 +116,22 @@ def opposite(self):
@property
def is_descending(self):
- """
- Returns `True` if this object induces *descending* ordering.
- """
+ """Return `True` if this object induces *descending* ordering."""
return self.startswith("-")
@property
def is_ascending(self):
- """
- Returns `True` if this object induces *ascending* ordering.
- """
+ """Return `True` if this object induces *ascending* ordering."""
return not self.is_descending
def for_queryset(self):
- """
- Returns the current instance usable in Django QuerySet's order_by
- arguments.
- """
+ """Return the current instance usable in Django QuerySet's order_by arguments."""
return self.replace(Accessor.LEGACY_SEPARATOR, OrderBy.QUERYSET_SEPARATOR)
class OrderByTuple(tuple):
"""
- Stores ordering as (as `.OrderBy` objects).
+ Store ordering as (as `.OrderBy` objects).
The `~.Table.order_by` property is always converted to an `.OrderByTuple` object.
This class is essentially just a `tuple` with some useful extras.
@@ -176,10 +172,12 @@ def __contains__(self, name):
True
Arguments:
- name (str): The name of a column. (optionally prefixed)
+ ---------
+ name (str): The name of a column. (optionally prefixed)
Returns:
- bool: `True` if the column with `name` influences the ordering.
+ -------
+ bool: `True` if the column with `name` influences the ordering.
"""
name = OrderBy(name).bare
for order_by in self:
@@ -189,10 +187,9 @@ def __contains__(self, name):
def __getitem__(self, index):
"""
- Allows an `.OrderBy` object to be extracted via named or integer
- based indexing.
+ Allow an `.OrderBy` object to be extracted via named or integer based indexing.
- When using named based indexing, it's fine to used a prefixed named::
+ When using name based indexing, it's fine to used a prefixed names::
>>> x = OrderByTuple(('name', '-age'))
>>> x[0]
@@ -203,10 +200,12 @@ def __getitem__(self, index):
'-age'
Arguments:
- index (int): Index to query the ordering for.
+ ---------
+ index (int): Index to query the ordering for.
Returns:
- `.OrderBy`: for the ordering at the index.
+ -------
+ `.OrderBy`: for the ordering at the index.
"""
if isinstance(index, str):
for order_by in self:
@@ -264,9 +263,7 @@ def __lt__(self, other):
return Comparator
def get(self, key, fallback):
- """
- Identical to `__getitem__`, but supports fallback value.
- """
+ """Identical to `__getitem__`, but supports fallback value."""
try:
return self[key]
except (KeyError, IndexError):
@@ -275,7 +272,9 @@ def get(self, key, fallback):
@property
def opposite(self):
"""
- Return version with each `.OrderBy` prefix toggled::
+ Return version with each `.OrderBy` prefix toggled.
+
+ Example::
>>> order_by = OrderByTuple(('name', '-age'))
>>> order_by.opposite
@@ -348,16 +347,18 @@ def resolve(self, context, safe=True, quiet=False):
Arguments:
- context : The root/first object to traverse.
- safe (bool): Don't call anything with `alters_data = True`
- quiet (bool): Smother all exceptions and instead return `None`
+ ---------
+ context : The root/first object to traverse.
+ safe (bool): Don't call anything with `alters_data = True`
+ quiet (bool): Smother all exceptions and instead return `None`
Returns:
- target object
+ -------
+ target object
Raises:
- TypeError`, `AttributeError`, `KeyError`, `ValueError`
- (unless `quiet` == `True`)
+ ------
+ TypeError`, `AttributeError`, `KeyError`, `ValueError` (unless `quiet` == `True`)
"""
# Short-circuit if the context contains a key with the exact name of the accessor,
# supporting list-of-dicts data returned from values_list("related_model__field")
@@ -412,9 +413,7 @@ def bits(self):
return self.split(self.SEPARATOR)
def get_field(self, model):
- """
- Return the django model field for model in context, following relations.
- """
+ """Return the django model field for model in context, following relations."""
if not hasattr(model, "_meta"):
return
@@ -435,7 +434,7 @@ def penultimate(self, context, quiet=True):
"""
Split the accessor on the right-most separator ('__'), return a tuple with:
- the resolved left part.
- - the remainder
+ - the remainder.
Example::
@@ -473,9 +472,7 @@ def as_html(self):
"""
Render to HTML tag attributes.
- Example:
-
- .. code-block:: python
+ Example::
>>> from django_tables2.utils import AttributeDict
>>> attrs = AttributeDict({'class': 'mytable', 'id': 'someid'})
@@ -490,11 +487,10 @@ def as_html(self):
def segment(sequence, aliases):
"""
- Translates a flat sequence of items into a set of prefixed aliases.
+ Translate a flat sequence of items into a set of prefixed aliases.
- This allows the value set by `.QuerySet.order_by` to be translated into
- a list of columns that would have the same result. These are called
- "order by aliases" which are optionally prefixed column names::
+ This allows the value set by `.QuerySet.order_by` to be translated into a list of columns that would have the same
+ result. These are called "order by aliases" which are optionally prefixed column names::
>>> list(segment(('a', '-b', 'c'),
... {'x': ('a'),
@@ -527,14 +523,16 @@ def segment(sequence, aliases):
def signature(fn):
"""
- Returns:
- tuple: Returns a (arguments, kwarg_name)-tuple:
- - the arguments (positional or keyword)
- - the name of the ** kwarg catch all.
+ Return the signature of the provided function.
+
+ Returns
+ -------
+ tuple: Returns a (arguments, kwarg_name)-tuple:
+ - the arguments (positional or keyword)
+ - the name of the ** kwarg catch all.
The self-argument for methods is always removed.
"""
-
signature = inspect.signature(fn)
args = []
@@ -552,13 +550,10 @@ def signature(fn):
def call_with_appropriate(fn, kwargs):
"""
- Calls the function ``fn`` with the keyword arguments from ``kwargs`` it expects
-
- If the kwargs argument is defined, pass all arguments, else provide exactly
- the arguments wanted.
+ Call the function ``fn`` with the keyword arguments from ``kwargs`` it expects.
- If one of the arguments of ``fn`` are not contained in kwargs, ``fn`` will not
- be called and ``None`` will be returned.
+ If the kwargs argument is defined, pass all arguments, else provide exactly the arguments wanted.
+ If one of the arguments of ``fn`` is missing from kwargs, ``fn`` will not be called and ``None`` will be returned.
"""
args, kwargs_name = signature(fn)
# no catch-all defined, we need to exactly pass the arguments specified.
@@ -574,7 +569,7 @@ def call_with_appropriate(fn, kwargs):
def computed_values(d, kwargs=None):
"""
- Returns a new `dict` that has callable values replaced with the return values.
+ Return a new `dict` that has callable values replaced with the return values.
Example::
@@ -603,11 +598,12 @@ def computed_values(d, kwargs=None):
{'name': 'Brad', 'parents': {'father': 'Foo', 'mother': 'Bar'}}
Arguments:
- d (dict): The original dictionary.
- kwargs: any extra keyword arguments will be passed to the callables, if the callable
- takes an argument with such a name.
+ ---------
+ d (dict): The original dictionary.
+ kwargs: extra keyword arguments will be passed callables, if the callable expects an argument with such a name.
Returns:
+ -------
dict: with callable values replaced.
"""
kwargs = kwargs or {}
diff --git a/django_tables2/views.py b/django_tables2/views.py
index 841bf901..066c6430 100644
--- a/django_tables2/views.py
+++ b/django_tables2/views.py
@@ -1,5 +1,5 @@
from itertools import count
-from typing import Any, Dict, Optional
+from typing import Any
from django.core.exceptions import ImproperlyConfigured
from django.views.generic.list import ListView
@@ -9,25 +9,22 @@
class TableMixinBase:
- """
- Base mixin for the Single- and MultiTable class based views.
- """
+ """Base mixin for the Single- and MultiTable class based views."""
context_table_name = "table"
table_pagination = None
def get_context_table_name(self, table):
- """
- Get the name to use for the table's template variable.
- """
+ """Get the name to use for the table's template variable."""
return self.context_table_name
def get_table_pagination(self, table):
"""
- Return pagination options passed to `.RequestConfig`:
- - True for standard pagination (default),
- - False for no pagination,
- - a dictionary for custom pagination.
+ Return pagination options passed to `.RequestConfig`.
+
+ - True for standard pagination (default),
+ - False for no pagination,
+ - a dictionary for custom pagination.
`ListView`s pagination attributes are taken into account, if `table_pagination` does not
define the corresponding value.
@@ -61,15 +58,17 @@ def get_table_pagination(self, table):
return paginate
- def get_paginate_by(self, table_data) -> Optional[int]:
+ def get_paginate_by(self, table_data) -> int | None:
"""
- Determines the number of items per page, or ``None`` for no pagination.
+ Determine the number of items per page, or ``None`` for no pagination.
Args:
- table_data: The table's data.
+ ----
+ table_data: The table's data.
Returns:
- Optional[int]: Items per page or ``None`` for no pagination.
+ -------
+ Optional[int]: Items per page or ``None`` for no pagination.
"""
return getattr(self, "paginate_by", None)
@@ -79,20 +78,18 @@ class SingleTableMixin(TableMixinBase):
Adds a Table object to the context. Typically used with
`.TemplateResponseMixin`.
- Attributes:
- table_class: subclass of `.Table`
- table_data: data used to populate the table, any compatible data source.
- context_table_name(str): name of the table's template variable (default:
- 'table')
- table_pagination (dict): controls table pagination. If a `dict`, passed as
- the *paginate* keyword argument to `.RequestConfig`. As such, any
- Truthy value enables pagination. (default: enable pagination).
+ Attributes
+ ----------
+ table_class: subclass of `.Table`
+ table_data: data used to populate the table, any compatible data source.
+ context_table_name(str): name of the table's template variable (default: 'table')
+ table_pagination (dict): controls table pagination. If a `dict`, passed as the *paginate* keyword argument to
+ `.RequestConfig`. As such, any truthy value enables pagination. (default: enable pagination).
- The `dict` can be used to specify values for arguments for the call to
- `~.tables.Table.paginate`.
+ The `dict` can be used to specify values for arguments for the call to `~.tables.Table.paginate`.
- If you want to use a non-standard paginator for example, you can add a key
- `paginator_class` to the dict, containing a custom `Paginator` class.
+ If you want to use a non-standard paginator for example, you can add a key `paginator_class` to the dict,
+ containing a custom `Paginator` class.
This mixin plays nice with the Django's ``.MultipleObjectMixin`` by using
``.get_queryset`` as a fall back for the table data source.
@@ -102,9 +99,7 @@ class SingleTableMixin(TableMixinBase):
table_data = None
def get_table_class(self):
- """
- Return the class to use for the table.
- """
+ """Return the class to use for the table."""
if self.table_class:
return self.table_class
if self.model:
@@ -125,9 +120,7 @@ def get_table(self, **kwargs):
)
def get_table_data(self):
- """
- Return the table data that should be used to populate the rows.
- """
+ """Return the table data that should be used to populate the rows."""
if self.table_data is not None:
return self.table_data
elif hasattr(self, "object_list"):
@@ -152,7 +145,7 @@ def get_table_kwargs(self):
"""
return {}
- def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
+ def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
"""
Overridden version of `.TemplateResponseMixin` to inject the table into
the template's context.
@@ -182,7 +175,8 @@ class MultiTableMixin(TableMixinBase):
having an entry containing the data for each table in `tables`, or by
overriding this method in order to return this data.
- Attributes:
+ Attributes
+ ----------
tables: list of `.Table` instances or list of `.Table` child objects.
tables_data: if defined, `tables` is assumed to be a list of table
classes which will be instantiated with the corresponding item from
@@ -205,9 +199,7 @@ class MultiTableMixin(TableMixinBase):
context_table_name = "tables"
def get_tables(self):
- """
- Return an array of table instances containing data.
- """
+ """Return an array of table instances containing data."""
if self.tables is None:
view_name = type(self).__name__
raise ImproperlyConfigured(f"No tables were specified. Define {view_name}.tables")
@@ -222,12 +214,10 @@ def get_tables(self):
return list(Table(data[i]) for i, Table in enumerate(self.tables))
def get_tables_data(self):
- """
- Return an array of table_data that should be used to populate each table
- """
+ """Return an array of table_data that should be used to populate each table."""
return self.tables_data
- def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
+ def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
tables = self.get_tables()
diff --git a/example/app/migrations/0001_initial.py b/example/app/migrations/0001_initial.py
index 0dc7318a..e2a06d7b 100644
--- a/example/app/migrations/0001_initial.py
+++ b/example/app/migrations/0001_initial.py
@@ -1,7 +1,7 @@
# Generated by Django 1.11.5 on 2017-09-22 13:23
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/example/app/migrations/0002_auto_20180416_0959.py b/example/app/migrations/0002_auto_20180416_0959.py
index 4662bdb4..fb791cf1 100644
--- a/example/app/migrations/0002_auto_20180416_0959.py
+++ b/example/app/migrations/0002_auto_20180416_0959.py
@@ -1,7 +1,7 @@
# Generated by Django 2.0.1 on 2018-04-16 09:59
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/example/app/models.py b/example/app/models.py
index b1e2c93a..4ace6b54 100644
--- a/example/app/models.py
+++ b/example/app/models.py
@@ -11,9 +11,7 @@ def __str__(self):
class Country(models.Model):
- """
- Represents a geographical Country
- """
+ """Represents a geographical Country."""
name = models.CharField(max_length=100)
population = models.PositiveIntegerField(verbose_name=_("population"))
diff --git a/example/app/views.py b/example/app/views.py
index 0bdcb6c9..28847b72 100644
--- a/example/app/views.py
+++ b/example/app/views.py
@@ -117,8 +117,7 @@ def checkbox(request):
def template_example(request, version):
- """Demonstrate the use of the bootstrap template"""
-
+ """Demonstrate the use of the bootstrap template."""
versions = {
"bootstrap3": (BootstrapTable, "bootstrap_template.html"),
"bootstrap4": (Bootstrap4Table, "bootstrap4_template.html"),
diff --git a/pyproject.toml b/pyproject.toml
index aa4949aa..9d73ef71 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,2 +1,39 @@
[tool.black]
line-length = 100
+
+[tool.ruff]
+fix = false
+fixable = [
+ "I001", # isort (sorting)
+ "F", # flake8
+ "D", # docformatter
+ "UP", # pyupgrade
+]
+ignore = [
+ "D1", # D1: Missing docstring error codes (because not every function and class has a docstring)
+ "D203", # D203: 1 blank line required before class docstring (conflicts with D211 and should be disabled, see https://github.com/PyCQA/pydocstyle/pull/91)
+ "D205",
+ "D212", # D212: Multi-line docstring summary should start at the first line
+]
+line-length = 120
+select = [
+ "D", # pydocstyle
+ "E", # pycodestyle
+ "F", # flake8
+ "I", # isort
+ "UP", # pyupgrade
+]
+target-version = "py311"
+unfixable = [
+ "F8", # names in flake8, such as defined but unused variables
+]
+
+[tool.ruff.per-file-ignores]
+"*/migrations/*" = ["D417", "E501"]
+
+[tool.djlint]
+# Reference: https://djlint.com/djlint/rules.html
+blank_line_after_tag = "load,extends,endblock,has_perm"
+ignore = "H006,H008,H013,H014,H017,H019,H021,H023,T001,T002,T003"
+max_line_length = 120
+profile = "django"
diff --git a/tests/app/migrations/0001_initial.py b/tests/app/migrations/0001_initial.py
index 3bebbd39..f8466de6 100644
--- a/tests/app/migrations/0001_initial.py
+++ b/tests/app/migrations/0001_initial.py
@@ -1,7 +1,7 @@
# Generated by Django 4.0a1 on 2021-09-22 18:51
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/tests/app/views.py b/tests/app/views.py
index 2e39d97e..5cf2e32f 100644
--- a/tests/app/views.py
+++ b/tests/app/views.py
@@ -5,7 +5,7 @@
def person(request, pk):
- """A really simple view to provide an endpoint for the 'person' URL."""
+ """View to serve as an endpoint for the 'person' URL."""
person = get_object_or_404(Person, pk=pk)
return HttpResponse(f"Person: {person}")
diff --git a/tests/columns/test_booleancolumn.py b/tests/columns/test_booleancolumn.py
index a61c9a94..74874688 100644
--- a/tests/columns/test_booleancolumn.py
+++ b/tests/columns/test_booleancolumn.py
@@ -28,9 +28,7 @@ class Meta:
@skipIf(django_version < (2, 1, 0), "Feature added in django 2.1")
def test_should_use_nullability_for_booloanfield(self):
- """
- Django 2.1 supports null=(True|False) for BooleanField.
- """
+ """Django 2.1 supports null=(True|False) for BooleanField."""
class BoolModel2(models.Model):
field = models.BooleanField(null=True)
@@ -98,10 +96,7 @@ class Table(tables.Table):
self.assertIn(table.rows[0].get_cell("col"), table.rows[0].get_cell("col_linkify"))
def test_boolean_field_choices_with_real_model_instances(self):
- """
- If a booleanField has choices defined, the value argument passed to
- BooleanColumn.render() is the rendered value, not a bool.
- """
+ """With choices defined, BooleanColumn.render() should get the rendered value, not a bool."""
class BoolModelChoices(models.Model):
field = models.BooleanField(choices=((True, "Yes"), (False, "No")))
@@ -119,7 +114,7 @@ class Meta:
self.assertEqual(table.rows[1].get_cell("field"), '✘')
def test_boolean_field_choices_spanning_relations(self):
- "The inverse lookup voor boolean choices should also work on related models"
+ """The inverse lookup for boolean choices should also work on related models."""
class Table(tables.Table):
boolean = tables.BooleanColumn(accessor="occupation__boolean_with_choices")
@@ -141,7 +136,7 @@ class Meta:
self.assertEqual(table.rows[1].get_cell("boolean"), '✘')
def test_boolean_should_not_prevent_rendering_of_other_columns(self):
- """Test for issue 360"""
+ """Test for issue 360."""
class Table(tables.Table):
boolean = tables.BooleanColumn(yesno="waar,onwaar")
diff --git a/tests/columns/test_datecolumn.py b/tests/columns/test_datecolumn.py
index 73ff95d1..8ee7632b 100644
--- a/tests/columns/test_datecolumn.py
+++ b/tests/columns/test_datecolumn.py
@@ -15,7 +15,7 @@ class DateColumnTest(SimpleTestCase):
Format string: https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date
D -- Day of the week, textual, 3 letters -- 'Fri'
b -- Month, textual, 3 letters, lowercase -- 'jan'
- Y -- Year, 4 digits. -- '1999'
+ Y -- Year, 4 digits. -- '1999'.
"""
def test_should_handle_explicit_format(self):
diff --git a/tests/columns/test_datetimecolumn.py b/tests/columns/test_datetimecolumn.py
index 7539a9b3..b7afb786 100644
--- a/tests/columns/test_datetimecolumn.py
+++ b/tests/columns/test_datetimecolumn.py
@@ -19,7 +19,7 @@ class DateTimeColumnTest(SimpleTestCase):
b -- Month, textual, 3 letters, lowercase -- 'jan'
Y -- Year, 4 digits. -- '1999'
A -- 'AM' or 'PM'. -- 'AM'
- f -- Time, in 12-hour hours[:minutes] -- '1', '1:30'
+ f -- Time, in 12-hour hours[:minutes] -- '1', '1:30'.
"""
def dt(self):
diff --git a/tests/columns/test_filecolumn.py b/tests/columns/test_filecolumn.py
index 278dd3da..f7d8b6be 100644
--- a/tests/columns/test_filecolumn.py
+++ b/tests/columns/test_filecolumn.py
@@ -12,7 +12,7 @@
def storage():
- """Provide a storage that exposes the test templates"""
+ """Provide a storage that exposes the test templates."""
root = os.path.join(os.path.dirname(__file__), "..", "app", "templates")
return FileSystemStorage(location=root, base_url="/baseurl/")
diff --git a/tests/columns/test_general.py b/tests/columns/test_general.py
index 4cd44225..44539e0c 100644
--- a/tests/columns/test_general.py
+++ b/tests/columns/test_general.py
@@ -156,9 +156,7 @@ class TranslationTable(tables.Table):
self.assertEqual(table.columns["text"].header, "Text")
def test_sequence(self):
- """
- Ensures that the sequence of columns is configurable.
- """
+ """Ensures that the sequence of columns is configurable."""
class TestTable(tables.Table):
a = tables.Column()
@@ -280,8 +278,11 @@ class SimpleTable(tables.Table):
table = SimpleTable([{"a": "value"}])
root = parse(table.as_html(request))
- # return classes of an element as a set
- classes = lambda x: set(x.attrib.get("class", "").split())
+
+ def classes(x):
+ """Return classes of an element as a set."""
+ return set(x.attrib.get("class", "").split())
+
self.assertIn("orderable", classes(root.findall(".//thead/tr/th")[0]))
self.assertNotIn("orderable", classes(root.findall(".//thead/tr/th")[1]))
@@ -324,9 +325,7 @@ class Table(tables.Table):
row[table]
def test_related_fields_get_correct_type(self):
- """
- Types of related fields should also lead to the correct type of column.
- """
+ """Types of related fields should also lead to the correct type of column."""
class PersonTable(tables.Table):
class Meta:
@@ -358,7 +357,7 @@ class Meta:
class ColumnInheritanceTest(TestCase):
def test_column_params_should_be_preserved_under_inheritance(self):
"""
- Github issue #337
+ Github issue #337.
Columns explicitly defined on MyTable get overridden by columns implicitly
defined on it's child.
@@ -394,9 +393,7 @@ class Meta(MyTable.Meta):
def test_explicit_column_can_be_overridden_by_other_explicit_column(self):
class MyTableC(MyTable):
- """
- If we define a new explict item1 column, that one should be used.
- """
+ """If we define a new explict item1 column, that one should be used."""
item1 = tables.Column(verbose_name="New nice column name")
@@ -409,7 +406,7 @@ class MyTableC(MyTable):
def test_override_column_class_names(self):
"""
We control the output of CSS class names for a column by overriding
- get_column_class_names
+ get_column_class_names.
"""
class MyTable(tables.Table):
@@ -436,7 +433,7 @@ def setUp(self):
Person.objects.create(first_name="Sjon", last_name="Jansen")
def test_computable_td_attrs(self):
- """Computable attrs for columns, using table argument"""
+ """Computable attrs for columns, using table argument."""
class Table(tables.Table):
person = tables.Column(attrs={"cell": {"data-length": lambda table: len(table.data)}})
@@ -453,7 +450,7 @@ class Table(tables.Table):
self.assertIn('
', html)
def test_computable_td_attrs_defined_in_column_class_attribute(self):
- """Computable attrs for columns, using custom Column"""
+ """Computable attrs for columns, using custom Column."""
class MyColumn(tables.Column):
attrs = {"td": {"data-test": lambda table: len(table.data)}}
@@ -469,7 +466,7 @@ class Table(tables.Table):
self.assertEqual(root.findall(".//tbody/tr/td")[1].attrib, {"data-test": "2"})
def test_computable_td_attrs_defined_in_column_class_attribute_record(self):
- """Computable attrs for columns, using custom column"""
+ """Computable attrs for columns, using custom column."""
class PersonColumn(tables.Column):
attrs = {
diff --git a/tests/columns/test_linkcolumn.py b/tests/columns/test_linkcolumn.py
index 01d3da83..123095d9 100644
--- a/tests/columns/test_linkcolumn.py
+++ b/tests/columns/test_linkcolumn.py
@@ -12,7 +12,7 @@
class LinkColumnTest(TestCase):
def test_unicode(self):
- """Test LinkColumn for unicode values + headings"""
+ """Test LinkColumn for unicode values + headings."""
class UnicodeTable(tables.Table):
first_name = tables.LinkColumn("person", args=[A("pk")])
@@ -77,7 +77,7 @@ class PersonTable(tables.Table):
self.assertIn("
—
", html)
def test_linkcolumn_non_field_based(self):
- """Test for issue 257, non-field based columns"""
+ """Test for issue 257, non-field based columns."""
class Table(tables.Table):
first_name = tables.Column()
@@ -141,8 +141,7 @@ class TestTable(tables.Table):
self.assertEqual(table.rows[0].get_cell("col"), table.rows[0].get_cell("col_linkify"))
def test_td_attrs_should_be_supported(self):
- """LinkColumn should support both