``.
@@ -247,14 +229,13 @@ def items(self):
class BoundPinnedRow(BoundRow):
- """
- Represents a *pinned* row in a table.
- """
+ """A *pinned* row in a table."""
@property
def attrs(self):
"""
Return the attributes for a certain pinned row.
+
Add CSS classes `pinned-row` and `odd` or `even` to `class` attribute.
Return:
@@ -328,10 +309,7 @@ def __len__(self):
return length
def __getitem__(self, key):
- """
- Slicing returns a new `~.BoundRows` instance, indexing returns a single
- `~.BoundRow` instance.
- """
+ """Return a new `~.BoundRows` instance for a slice, a single `~.BoundRow` instance for an index."""
if isinstance(key, slice):
return BoundRows(data=self.data[key], table=self.table, pinned_data=self.pinned_data)
else:
diff --git a/django_tables2/tables.py b/django_tables2/tables.py
index 10dea6e6..8336439f 100644
--- a/django_tables2/tables.py
+++ b/django_tables2/tables.py
@@ -154,9 +154,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
@@ -372,9 +370,9 @@ def __init__(
def get_top_pinned_data(self):
"""
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 result set message being rendered,
+ 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:
@@ -396,9 +394,9 @@ 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,
+ 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:
@@ -419,7 +417,7 @@ def get_bottom_pinned_data(self):
def before_render(self, request):
"""
- A way to hook into the moment just before rendering the template.
+ Perform an action just before rendering the template.
Can be used to hide a column.
@@ -443,9 +441,7 @@ def before_render(self, request):
return
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,18 +453,15 @@ 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:
+ 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`.
+ This can be used to output the table data as CSV, excel, for example using the `~.export.ExportMixin`.
- If a column is defined using a :ref:`table.render_FOO`, the returned value from
- that method is used. If you want to differentiate between the rendered cell
- and a value, use a `value_Foo`-method::
+ If a column is defined using a :ref:`table.render_FOO`, the returned value from that method is used.
+ If you want to differentiate between the rendered cell and a value, use a `value_Foo`-method::
class Table(tables.Table):
name = tables.Column()
@@ -501,10 +494,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
@@ -561,8 +551,7 @@ 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 using a paginator and creates a `page` property containing information for the current page.
Arguments:
paginator_class (`~django.core.paginator.Paginator`): A paginator class to
@@ -570,14 +559,12 @@ def paginate(self, paginator_class=Paginator, per_page=None, page=1, *args, **kw
per_page (int): Number of records to display on each page.
page (int): Page to display.
+ *args: passed on to the paginator.
+ **kwargs: passed on to the paginator.
- 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,21 +636,17 @@ 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
@@ -695,8 +678,10 @@ 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
diff --git a/django_tables2/templatetags/django_tables2.py b/django_tables2/templatetags/django_tables2.py
index cfcf5276..8439432c 100644
--- a/django_tables2/templatetags/django_tables2.py
+++ b/django_tables2/templatetags/django_tables2.py
@@ -26,8 +26,7 @@
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.
- No legacy mode.
- Both keys and values are compiled as a filter
@@ -81,9 +80,9 @@ 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.
+ Create an URL (containing only the query string [including "?"]) derivedfrom the current URL's query string.
+
+ By updating it with the provided keyword arguments.
Example (imagine URL is ``/abc/?gender=male&name=Brad``)::
@@ -119,9 +118,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
+
"""
def __init__(self, table, template_name=None):
@@ -213,8 +215,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 +225,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)
@@ -238,7 +238,8 @@ def export_url(context, export_format, export_trigger_param=None):
@register.filter
def table_page_range(page, paginator):
"""
- Given an page and paginator, return a list of max 10 (by default) page numbers:
+ Given an page and paginator, return a list of max 10 (by default) page numbers.
+
- always containing the first, last and current page.
- containing one or two '...' to skip ranges between first/last and current.
@@ -247,7 +248,6 @@ def table_page_range(page, 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 33efabc9..c15aa53a 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')``
+ 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,15 +24,15 @@ 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:
+ Arguments:
columns (list): list of column names.
- returns:
+
+ Returns:
The current instance.
- raises:
+ Raises:
`ValueError` if the sequence is invalid for the columns.
"""
ellipses = self.count("...")
@@ -83,11 +83,9 @@ def __new__(cls, value):
@property
def bare(self):
"""
- Returns:
- `.OrderBy`: the bare form.
+ Return the bare form, without the direction prefix.
- The *bare form* is the non-prefixed form. Typically the bare form is
- just the ascending form.
+ 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``
@@ -97,7 +95,7 @@ def bare(self):
@property
def opposite(self):
"""
- Provides the opposite of the current sorting direction.
+ Provide the opposite of the current sorting direction.
Returns:
`.OrderBy`: object with an opposite sort influence.
@@ -113,29 +111,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.
@@ -189,8 +180,7 @@ def __contains__(self, name):
def __getitem__(self, index):
"""
- Allows an `.OrderBy` object to be extracted via named or integer
- based indexing.
+ Extract an `.OrderBy` item by index.
When using named based indexing, it's fine to used a prefixed named::
@@ -264,9 +254,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 +263,7 @@ def get(self, key, fallback):
@property
def opposite(self):
"""
- Return version with each `.OrderBy` prefix toggled::
+ Return version with each `.OrderBy` prefix toggled.
>>> order_by = OrderByTuple(('name', '-age'))
>>> order_by.opposite
@@ -417,9 +405,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
@@ -438,7 +424,8 @@ def get_field(self, model):
def penultimate(self, context, quiet=True):
"""
- Split the accessor on the right-most separator ('__'), return a tuple with:
+ Split the accessor on the right-most separator ('__'), return a tuple with.
+
- the resolved left part.
- the remainder
@@ -495,7 +482,7 @@ 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
@@ -532,14 +519,13 @@ 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 an (arguments, kwargs)-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 = []
@@ -557,10 +543,9 @@ def signature(fn):
def call_with_appropriate(fn, kwargs):
"""
- Calls the function ``fn`` with the keyword arguments from ``kwargs`` it expects
+ Call 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.
+ If the kwargs argument is defined, pass all arguments, else provide exactly the arguments wanted.
If one of the arguments of ``fn`` are not contained in kwargs, ``fn`` will not
be called and ``None`` will be returned.
@@ -579,7 +564,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::
diff --git a/django_tables2/views.py b/django_tables2/views.py
index b972c0a0..0a6ccab3 100644
--- a/django_tables2/views.py
+++ b/django_tables2/views.py
@@ -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.
- """
+ """Return 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.
@@ -63,7 +60,7 @@ def get_table_pagination(self, table):
def get_paginate_by(self, table_data) -> Optional[int]:
"""
- 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.
@@ -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"):
@@ -153,10 +146,7 @@ def get_table_kwargs(self):
return {}
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
- """
- Overridden version of `.TemplateResponseMixin` to inject the table into
- the template's context.
- """
+ """Overridden version of `.TemplateResponseMixin` to inject the table into the template's context."""
context = super().get_context_data(**kwargs)
table = self.get_table(**self.get_table_kwargs())
context[self.get_context_table_name(table)] = table
@@ -205,9 +195,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,9 +210,7 @@ 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]:
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 b4f2d03c..2a2c860a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -44,9 +44,6 @@ Documentation = "https://django-tables2.readthedocs.io/en/latest/"
Homepage = "https://github.com/jieter/django-tables2/"
Readme = "https://github.com/jieter/django-tables2/blob/master/README.md"
-[tool.black]
-line-length = 100
-
[tool.hatch.build.targets.sdist]
exclude = ["docs"]
@@ -56,5 +53,46 @@ packages = ["django_tables2"]
[tool.hatch.version]
path = "django_tables2/__init__.py"
+[tool.ruff]
+line-length = 100
+target-version = "py39"
+
+[tool.ruff.lint]
+fixable = [
+ # "D",
+ "D200",
+ "D202",
+ "D401",
+ "D400",
+ "D415",
+ "E",
+ "F",
+ "I",
+ "UP"
+]
+ignore = [
+ "D1", # Missing docstring error codes (because not every function and class has a docstring)
+ "D203", # 1 blank line required before class docstring (conflicts with D211 and should be disabled, see https://github.com/PyCQA/pydocstyle/pull/91)
+ "D205",
+ "D406", # Section name should end with a newline
+ "D407", # Missing dashed underline after section
+ "D413", # Missing blank line after last section ...
+ "D212", # Multi-line docstring summary should start at the first line
+ "E501" # Line too long
+]
+select = [
+ "D", # pydocstyle
+ "E", # pycodestyle
+ "F", # flake8
+ "I", # isort
+ "UP" # pyupgrade
+]
+unfixable = [
+ "F8" # names in flake8, such as defined but unused variables
+]
+
+[tool.ruff.lint.per-file-ignores]
+"*/migrations/*" = ["D417", "E501"]
+
[tool.setuptools.dynamic]
version = {attr = "django_tables2.__version__"}
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..5cb57800 100644
--- a/tests/app/views.py
+++ b/tests/app/views.py
@@ -5,12 +5,12 @@
def person(request, pk):
- """A really simple view to provide an endpoint for the 'person' URL."""
+ """Endpoint for the 'person' URL."""
person = get_object_or_404(Person, pk=pk)
return HttpResponse(f"Person: {person}")
def occupation(request, pk):
- """Another really simple view to provide an endpoint for the 'occupation' URL."""
+ """Endpoint for the 'occupation' URL."""
occupation = get_object_or_404(Occupation, pk=pk)
return HttpResponse(f"Occupation: {occupation}")
diff --git a/tests/columns/test_booleancolumn.py b/tests/columns/test_booleancolumn.py
index a61c9a94..792f031b 100644
--- a/tests/columns/test_booleancolumn.py
+++ b/tests/columns/test_booleancolumn.py
@@ -1,6 +1,3 @@
-from unittest import skipIf
-
-from django import VERSION as django_version
from django.db import models
from django.test import TestCase
@@ -26,11 +23,8 @@ class Meta:
self.assertEqual(type(column), tables.BooleanColumn)
self.assertEqual(column.empty_values, ())
- @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)
@@ -99,6 +93,8 @@ class Table(tables.Table):
def test_boolean_field_choices_with_real_model_instances(self):
"""
+ Verify that choices are used if defined.
+
If a booleanField has choices defined, the value argument passed to
BooleanColumn.render() is the rendered value, not a bool.
"""
@@ -119,7 +115,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 voor boolean choices should also work on related models."""
class Table(tables.Table):
boolean = tables.BooleanColumn(accessor="occupation__boolean_with_choices")
@@ -141,7 +137,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 that other columns should render even if the boolean column receives a non-boolean value (#360)."""
class Table(tables.Table):
boolean = tables.BooleanColumn(yesno="waar,onwaar")
@@ -150,8 +146,8 @@ class Meta:
model = Occupation
fields = ("boolean", "name")
- Occupation.objects.create(name="Waar", boolean=True),
- Occupation.objects.create(name="Onwaar", boolean=False),
+ Occupation.objects.create(name="Waar", boolean=True)
+ Occupation.objects.create(name="Onwaar", boolean=False)
Occupation.objects.create(name="Onduidelijk")
html = Table(Occupation.objects.all()).as_html(build_request())
diff --git a/tests/columns/test_datecolumn.py b/tests/columns/test_datecolumn.py
index 73ff95d1..5d0121c3 100644
--- a/tests/columns/test_datecolumn.py
+++ b/tests/columns/test_datecolumn.py
@@ -12,6 +12,8 @@ def isoformat_link(value):
class DateColumnTest(SimpleTestCase):
"""
+ Date formatting test case.
+
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'
diff --git a/tests/columns/test_datetimecolumn.py b/tests/columns/test_datetimecolumn.py
index 7539a9b3..52a1cad0 100644
--- a/tests/columns/test_datetimecolumn.py
+++ b/tests/columns/test_datetimecolumn.py
@@ -14,6 +14,8 @@ def isoformat_link(value):
class DateTimeColumnTest(SimpleTestCase):
"""
+ Date time formatting test case.
+
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'
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..6944c0c7 100644
--- a/tests/columns/test_general.py
+++ b/tests/columns/test_general.py
@@ -144,10 +144,7 @@ class SimpleTable(tables.Table):
self.assertFalse(table.columns["name"].is_ordered)
def test_translation(self):
- """
- Tests different types of values for the ``verbose_name`` property of a
- column.
- """
+ """Tests different types of values for the ``verbose_name`` property of a column."""
class TranslationTable(tables.Table):
text = tables.Column(verbose_name=gettext_lazy("Text"))
@@ -156,9 +153,7 @@ class TranslationTable(tables.Table):
self.assertEqual(table.columns["text"].header, "Text")
def test_sequence(self):
- """
- Ensures that the sequence of columns is configurable.
- """
+ """Ensure that the sequence of columns is configurable."""
class TestTable(tables.Table):
a = tables.Column()
@@ -280,18 +275,20 @@ 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())
- self.assertIn("orderable", classes(root.findall(".//thead/tr/th")[0]))
- self.assertNotIn("orderable", classes(root.findall(".//thead/tr/th")[1]))
+ def get_classes(element) -> set[str]:
+ return set(element.attrib.get("class", "").split())
+
+ self.assertIn("orderable", get_classes(root.findall(".//thead/tr/th")[0]))
+ self.assertNotIn("orderable", get_classes(root.findall(".//thead/tr/th")[1]))
# Now try with an ordered table
table = SimpleTable([], order_by="a")
root = parse(table.as_html(request))
- # return classes of an element as a set
- self.assertIn("orderable", classes(root.findall(".//thead/tr/th")[0]))
- self.assertIn("asc", classes(root.findall(".//thead/tr/th")[0]))
- self.assertNotIn("orderable", classes(root.findall(".//thead/tr/th")[1]))
+ self.assertIn("orderable", get_classes(root.findall(".//thead/tr/th")[0]))
+ self.assertIn("asc", get_classes(root.findall(".//thead/tr/th")[0]))
+ self.assertNotIn("orderable", get_classes(root.findall(".//thead/tr/th")[1]))
def test_empty_values_triggers_default(self):
class Table(tables.Table):
@@ -324,9 +321,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,10 +353,9 @@ class Meta:
class ColumnInheritanceTest(TestCase):
def test_column_params_should_be_preserved_under_inheritance(self):
"""
- Github issue #337
+ Column parameters should be preserved under inheritance (#337).
- Columns explicitly defined on MyTable get overridden by columns implicitly
- defined on it's child.
+ Columns explicitly defined on MyTable get overridden by columns implicitly defined on it's child.
If the column is not redefined, the explicit definition of MyTable is used,
preserving the specialized verbose_name defined on it.
"""
@@ -394,9 +388,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 explicit item1 column, that one should be used."""
item1 = tables.Column(verbose_name="New nice column name")
@@ -407,10 +399,7 @@ class MyTableC(MyTable):
self.assertEqual(tableC.columns["item1"].verbose_name, "New nice column name")
def test_override_column_class_names(self):
- """
- We control the output of CSS class names for a column by overriding
- get_column_class_names
- """
+ """We control the output of CSS class names for a column by overriding get_column_class_names()."""
class MyTable(tables.Table):
population = tables.Column(verbose_name="Population")
@@ -436,7 +425,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 +442,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 +458,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 = {
@@ -495,10 +484,7 @@ class Table(tables.Table):
)
def test_computable_column_td_attrs_record_header(self):
- """
- Computable attrs for columns, using custom column with a callable containing
- a catch-all argument.
- """
+ """Computable attrs for columns, using custom column with a callable containing a catch-all argument."""
def data_first_name(**kwargs):
record = kwargs.get("record", None)
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