# coding: utf-8
from __future__ import unicode_literals
from itertools import count
from django.core.exceptions import ImproperlyConfigured
from django.views.generic.list import ListView
from .config import RequestConfig
class TableMixinBase(object):
'''
Base mixin for table-related class based views.
'''
context_table_name = 'table'
table_pagination = None
def get_table_class(self):
'''
Return the class to use for the table.
'''
if self.table_class:
return self.table_class
klass = type(self).__name__
raise ImproperlyConfigured(
'A table class was not specified. Define {}.table_class'.format(klass)
)
def get_context_table_name(self, table):
'''
Get the name to use for the table's template variable.
'''
return self.context_table_name
def get_table_pagination(self, table):
'''
Returns pagination options: True for standard pagination (default),
False for no pagination, and a dictionary for custom pagination.
'''
paginate = self.table_pagination
if hasattr(self, 'paginate_by') and self.paginate_by is not None:
# Since ListView knows the concept paginate_by, we use that if no
# other pagination is configured.
paginate = paginate or {}
paginate['per_page'] = self.paginate_by
if paginate is None:
return True
return paginate
[docs]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)
This mixin plays nice with the Django's`.MultipleObjectMixin` by using
`.get_queryset`` as a fallback for the table data source.
'''
table_class = None
table_data = None
[docs] def get_table(self, **kwargs):
'''
Return a table object to use. The table has automatic support for
sorting and pagination.
'''
table_class = self.get_table_class()
table = table_class(data=self.get_table_data(), **kwargs)
RequestConfig(self.request, paginate=self.get_table_pagination(table)).configure(table)
return table
[docs] def get_table_data(self):
'''
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'):
return self.object_list
elif hasattr(self, 'get_queryset'):
return self.get_queryset()
klass = type(self).__name__
raise ImproperlyConfigured(
'Table data was not specified. Define {}.table_data'.format(klass)
)
[docs] 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 {}
[docs] def get_context_data(self, **kwargs):
'''
Overriden version of `.TemplateResponseMixin` to inject the table into
the template's context.
'''
context = super(SingleTableMixin, self).get_context_data(**kwargs)
table = self.get_table(**self.get_table_kwargs())
context[self.get_context_table_name(table)] = table
return context
[docs]class SingleTableView(SingleTableMixin, ListView):
'''
Generic view that renders a template and passes in a `.Table` instances.
'''
[docs]class MultiTableMixin(TableMixinBase):
'''
Adds a Table object to the context. Typically used with
`.TemplateResponseMixin`.
the `tables` attribute must be either a list of `.Table` instances or
classes extended from `.Table` which are not already instantiated. In that
case, tables_data must be defined, having an entry containing the data for
each table in `tables`.
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 instatiated with the corresponding item from
this list of `.TableData` instances.
table_prefix(str): Prefix to be used for each table. The string must
contain one instance of `{}`, which will be replaced by an integer
different for each table in the view. Default is 'table_{}-'.
context_table_name(str): name of the table's template variable (default:
'tables')
.. versionadded:: 1.2.3
'''
tables = None
tables_data = None
table_prefix = 'table_{}-'
# override context table name to make sense in a multiple table context
context_table_name = 'tables'
def get_tables(self):
if not self.tables:
klass = type(self).__name__
raise ImproperlyConfigured(
'No tables were specified. Define {}.tables'.format(klass)
)
return self.tables
def get_context_data(self, **kwargs):
context = super(MultiTableMixin, self).get_context_data(**kwargs)
if self.tables_data is None:
tables = self.get_tables()
else:
data = self.tables_data
if len(data) != len(self.get_tables()):
klass = type(self).__name__
raise ImproperlyConfigured(
'len({}.tables_data) != len({}.tables)'.format(klass, klass)
)
tables = list(Table(data[i]) for i, Table in enumerate(self.tables))
# apply prefixes and execute requestConfig for each table
table_counter = count()
for table in tables:
table.prefix = self.table_prefix.format(next(table_counter))
RequestConfig(self.request, paginate=self.get_table_pagination(table)).configure(table)
context[self.get_context_table_name(table)] = list(tables)
return context