=== modified file 'lava/utils/data_tables/backends.py'
@@ -17,6 +17,7 @@
# along with LAVA Server. If not, see <http://www.gnu.org/licenses/>.
from django.core.exceptions import ImproperlyConfigured
+from django.db.models import Q
from lava.utils.data_tables.interface import IBackend
@@ -63,10 +64,91 @@
# TODO: Support regex search
# TODO: Support per-column search
# 2) Apply sorting
- for column_index, order in reversed(euery.sorting_columns):
+ for column_index, order in reversed(query.sorting_columns):
data.sort(key=lambda row: row[column_index], reverse=order == 'desc')
# 3) Apply offset/limit
data = data[query.iDisplayStart:query.iDisplayStart + query.iDisplayLength]
# Remember the subset of the displayed data
response['aaData'] = data
return response
+
+
+class Column(object):
+ """
+ Column definition for the QuerySetBackend
+ """
+
+ def __init__(self, name, sort_expr, callback):
+ self.name = name
+ self.sort_expr = sort_expr
+ self.callback = callback
+
+
+class QuerySetBackend(_BackendBase):
+ """
+ Database backend for data tables.
+
+ Stores and processes/computes the data in the database. Needs a queryset
+ object and a mapping between colums and query values.
+ """
+
+ def __init__(self, queryset=None, queryset_cb=None, columns=None,
+ searching_columns=None):
+ self.queryset = queryset
+ self.queryset_cb = queryset_cb
+ self.columns = columns
+ self.searching_columns = searching_columns
+ if not queryset and not queryset_cb:
+ raise ImproperlyConfigured(
+ "QuerySetBackend requires either queryset or queryset_cb")
+ if not columns:
+ raise ImproperlyConfigured(
+ "QuerySetBackend requires columns")
+
+ def process(self, query):
+ # Get the basic response structure
+ response = super(QuerySetBackend, self).process(query)
+ # Get a queryset object
+ if self.queryset:
+ queryset = self.queryset
+ else:
+ queryset = self.queryset_cb(query.request)
+ # 1) Apply search/filtering
+ if query.sSearch:
+ if query.bRegex:
+ raise NotImplementedError("Searching with regular expresions is not implemented")
+ else:
+ if self.searching_columns is None:
+ raise NotImplementedError("Searching is not implemented")
+ terms = query.sSearch.split()
+ andQ = None
+ for term in terms:
+ orQ = None
+ for col in self.searching_columns:
+ q = Q(**{col+"__icontains" : term})
+ orQ = orQ | q if orQ else q
+ andQ = andQ & orQ if andQ else orQ
+ response['iTotalRecords'] = queryset.count()
+ queryset = queryset.filter(andQ)
+ response['iTotalDisplayRecords'] = queryset.count()
+ else:
+ response['iTotalRecords'] = response['iTotalDisplayRecords'] = queryset.count()
+ # TODO: Support per-column search
+ # 2) Apply sorting
+ order_by = [
+ "{asc_desc}{column}".format(
+ asc_desc="-" if order == 'desc' else '',
+ column=self.columns[column_index].sort_expr)
+ for column_index, order in query.sorting_columns]
+ queryset = queryset.order_by(*order_by)
+ # 3) Apply offset/limit
+ queryset = queryset[query.iDisplayStart:query.iDisplayStart + query.iDisplayLength]
+ # Compute the response
+ # Note, despite the 'aaData'' identifier we're
+ # returing aoData-typed result (array of objects)
+ # print queryset.values(*[column.filter_expr for column in self.columns])
+ response['aaData'] = [
+ dict([(column.name, column.callback(object))
+ for column in self.columns])
+ for object in queryset]
+ return response