def test_invalid_sorting_parameters(self): '''Should not validate invalid sorting parameters''' form = DatatablesForm({ 'sEcho': '1', 'iColumns': '5', 'iDisplayStart': '0', 'iDisplayLength': '10', 'sSearch': '', 'bRegex': 'false', 'iSortingCols': '1', }) self.assertFalse(form.is_valid())
def test_valid_extra_parameters(self): '''Should validate with extra parameters''' form = DatatablesForm({ 'sEcho': '1', 'iColumns': '5', 'iDisplayStart': '0', 'iDisplayLength': '10', 'sSearch': '', 'bRegex': 'false', 'iSortingCols': '1', 'mDataProp_0': '0', 'mDataProp_1': '1', 'mDataProp_2': '2', 'mDataProp_3': '3', 'mDataProp_4': '4', 'sSearch_0': 's0', 'sSearch_1': 's1', 'sSearch_2': 's2', 'sSearch_3': 's3', 'sSearch_4': 's4', 'bRegex_0': 'false', 'bRegex_1': 'false', 'bRegex_2': 'false', 'bRegex_3': 'false', 'bRegex_4': 'false', 'bSearchable_0': 'true', 'bSearchable_1': 'true', 'bSearchable_2': 'true', 'bSearchable_3': 'true', 'bSearchable_4': 'true', 'bSortable_0': 'true', 'bSortable_1': 'true', 'bSortable_2': 'true', 'bSortable_3': 'true', 'bSortable_4': 'true', 'iSortCol_0': '0', 'sSortDir_0': 'asc', }) self.assertTrue(form.is_valid()) for idx in range(5): self.assertEqual(form.cleaned_data['mDataProp_%s' % idx], '%s' % idx) self.assertEqual(form.cleaned_data['sSearch_%s' % idx], 's%s' % idx) self.assertEqual(form.cleaned_data['bRegex_%s' % idx], False) self.assertEqual(form.cleaned_data['bSearchable_%s' % idx], True) self.assertEqual(form.cleaned_data['bSortable_%s' % idx], True) self.assertEqual(form.cleaned_data['iSortCol_0'], 0) self.assertEqual(form.cleaned_data['sSortDir_0'], 'asc')
def test_valid_extra_parameters(self): '''Should validate with extra parameters''' form = DatatablesForm({ 'sEcho': '1', 'iColumns': '5', 'iDisplayStart': '0', 'iDisplayLength': '10', 'sSearch': '', 'bRegex': 'false', 'iSortingCols': '1', 'mDataProp_0': '0', 'mDataProp_1': '1', 'mDataProp_2': '2', 'mDataProp_3': '3', 'mDataProp_4': '4', 'sSearch_0': 's0', 'sSearch_1': 's1', 'sSearch_2': 's2', 'sSearch_3': 's3', 'sSearch_4': 's4', 'bRegex_0': 'false', 'bRegex_1': 'false', 'bRegex_2': 'false', 'bRegex_3': 'false', 'bRegex_4': 'false', 'bSearchable_0': 'true', 'bSearchable_1': 'true', 'bSearchable_2': 'true', 'bSearchable_3': 'true', 'bSearchable_4': 'true', 'bSortable_0': 'true', 'bSortable_1': 'true', 'bSortable_2': 'true', 'bSortable_3': 'true', 'bSortable_4': 'true', 'iSortCol_0': '0', 'sSortDir_0': 'asc', }) self.assertTrue(form.is_valid()) for idx in xrange(5): self.assertEqual(form.cleaned_data['mDataProp_%s' % idx], '%s' % idx) self.assertEqual(form.cleaned_data['sSearch_%s' % idx], 's%s' % idx) self.assertEqual(form.cleaned_data['bRegex_%s' % idx], False) self.assertEqual(form.cleaned_data['bSearchable_%s' % idx], True) self.assertEqual(form.cleaned_data['bSortable_%s' % idx], True) self.assertEqual(form.cleaned_data['iSortCol_0'], 0) self.assertEqual(form.cleaned_data['sSortDir_0'], 'asc')
def test_base_parameters(self): '''Should validate base parameters''' form = DatatablesForm({ 'sEcho': '1', 'iColumns': '5', 'iDisplayStart': '0', 'iDisplayLength': '10', 'sSearch': '', 'bRegex': 'false', 'iSortingCols': '1', 'iSortCol_0': '0', 'sSortDir_0': 'asc', }) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['sEcho'], '1') self.assertEqual(form.cleaned_data['iColumns'], 5) self.assertEqual(form.cleaned_data['iDisplayStart'], 0) self.assertEqual(form.cleaned_data['iDisplayLength'], 10) self.assertEqual(form.cleaned_data['sSearch'], '') self.assertEqual(form.cleaned_data['bRegex'], False) self.assertEqual(form.cleaned_data['iSortingCols'], 1)
class DatatablesView(MultipleObjectMixin, View): ''' Render a paginated server-side Datatables JSON view. See: http://www.datatables.net/usage/server-side ''' fields = [] _db_fields = None ServerSide = True _formatted_fields = False filters = {} def post(self, request, *args, **kwargs): return self.process_dt_response(request.POST) def get(self, request, *args, **kwargs): return self.process_dt_response(request.GET) def process_dt_response(self, data): # Switch between server-side and client-side mode. Given that # 'iColumns' is a needed server-side parameter, if it doesn't exist we # can safely switch to client-side. if 'iColumns' in data: self.generate_search_sets(data) self.form = DatatablesForm(data) if not self.form.is_valid(): return HttpResponseBadRequest() else: self.form = None self.ServerSide = False self.qs = self.get_queryset() self.set_object_list() return self.render_to_response(self.form) def set_object_list(self): if isinstance(self.fields, dict): self.object_list = self.qs.values(*self.get_db_fields()) else: self.object_list = self.qs.values_list(*self.get_db_fields()) def get_db_fields(self): if not self._db_fields: self._db_fields = [] fields = self.fields.values() if isinstance(self.fields, dict) else self.fields for field in fields: if RE_FORMATTED.match(field): self._formatted_fields = True self._db_fields.extend(RE_FORMATTED.findall(field)) else: self._db_fields.append(field) return self._db_fields @property def dt_data(self): return self.form.cleaned_data def get_field(self, index): if isinstance(self.fields, dict): return self.fields[self.dt_data['mDataProp_%s' % index]] else: return self.fields[index] def can_regex(self, field): '''Test if a given field supports regex lookups''' from django.conf import settings if settings.DATABASES['default']['ENGINE'].endswith('sqlite3'): return not isinstance(get_real_field(self.model, field), UNSUPPORTED_REGEX_FIELDS) else: return True def generate_search_sets(self, request): """Generate search sets from DataTables request. Search sets are lists of key-value pairs (in tuple format) that define the search space for the column search function and custom filter functions. These lists are generated from the DataTables HTTP request using the following method: * If the request, has an argument that starts with "sSearch_" and whose value is not empty, then we decide on which search set it will be added. * The arguments that are added in the column search set must have as suffix a number that corresponds to a table column. * All other arguments are added in the filter search set. Note: The reason why we use the values of the HTTP request instead of the values of form.cleaned_data is because we cannot always know the filter name, i.e. what is followed after "sSearch_", which means that we cannot create a form field for it. """ self.column_set = [] self.filter_set = {} for param, value in request.iteritems(): if not param.startswith("sSearch_"): continue if not value: continue _, search = param.split("_", 1) try: column_idx = int(search) if column_idx < request['iColumns']: self.column_set.append((column_idx, value), ) else: self.filter_set[search] = value except ValueError: if '[]' in search: value = request.getlist(param) search = search.replace('[]', '') self.filter_set[search] = value def get_orders(self): '''Get ordering fields for ``QuerySet.order_by``''' orders = [] iSortingCols = self.dt_data['iSortingCols'] dt_orders = [(self.dt_data['iSortCol_%s' % i], self.dt_data['sSortDir_%s' % i]) for i in xrange(iSortingCols)] for field_idx, field_dir in dt_orders: direction = '-' if field_dir == DESC else '' if hasattr(self, 'sort_col_%s' % field_idx): method = getattr(self, 'sort_col_%s' % field_idx) result = method(direction) if isinstance(result, (bytes, text_type)): orders.append(result) else: orders.extend(result) else: field = self.get_field(field_idx) if RE_FORMATTED.match(field): tokens = RE_FORMATTED.findall(field) orders.extend( ['%s%s' % (direction, token) for token in tokens]) else: orders.append('%s%s' % (direction, field)) return orders def global_search(self, queryset): '''Filter a queryset with global search''' search = self.dt_data['sSearch'] if search: if self.dt_data['bRegex']: criterions = [ Q(**{'%s__iregex' % field: search}) for field in self.get_db_fields() if self.can_regex(field) ] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields()) search = reduce(or_, criterions) queryset = queryset.filter(search) return queryset def column_search(self, queryset): '''Filter a queryset with column search''' for idx, search in self.column_set: if hasattr(self, 'search_col_%s' % idx): custom_search = getattr(self, 'search_col_%s' % idx) queryset = custom_search(search, queryset) else: field = self.get_field(idx) fields = RE_FORMATTED.findall(field) if RE_FORMATTED.match( field) else [field] if self.dt_data['bRegex_%s' % idx]: criterions = [ Q(**{'%s__iregex' % field: search}) for field in fields if self.can_regex(field) ] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in fields) search = reduce(or_, criterions) queryset = queryset.filter(search) return queryset def get_filters(self): if hasattr(self, "_filters"): return if isinstance(self.filters, list): self._filters = {filter.name: filter for filter in self.filters} # We can't check if the provided type is FilterSet, unless we try to # import it, which would not be a clean solution. Therefore, anything # that's not a list, will be stored in self._filters. else: self._filters = self.filters def custom_filtering(self, queryset): if not self.filter_set: return queryset self.get_filters() # If the following succeeds, then the user has provided as a # django-filter FilterSet and we don't actually need to iterate the # filters. try: return self._filters(data=self.filter_set, queryset=queryset).qs except TypeError: pass for attr, query in self.filter_set.iteritems(): if hasattr(self, "filter_%s" % attr): custom_filter = getattr(self, "filter_%s" % attr) queryset = custom_filter(queryset, query) else: try: f = self._filters[attr] queryset = f.filter(queryset, query) except KeyError: raise Exception('Unsupported filter: %s' % attr) return queryset def get_queryset(self): '''Apply Datatables sort and search criterion to QuerySet''' qs = super(DatatablesView, self).get_queryset() # Bail if we are not in server-side mode if not self.ServerSide: return qs # Perform global search qs = self.global_search(qs) # Perform column search qs = self.column_search(qs) # Perform custom filtering qs = self.custom_filtering(qs) # Return the ordered queryset return qs.order_by(*self.get_orders()) def get_page(self, form, object_list): '''Get the requested page''' page_size = form.cleaned_data['iDisplayLength'] start_index = form.cleaned_data['iDisplayStart'] paginator = Paginator(object_list, page_size) num_page = (start_index / page_size) + 1 return paginator.page(num_page) def get_rows(self, rows): '''Format all rows''' if self._formatted_fields: return map(self.get_row, rows) else: if isinstance(self.fields, dict): return [{ key: row[value] for key, value in self.fields.iteritems() } for row in rows] else: return list(rows) def get_row(self, row): '''Format a single row (if necessary)''' if isinstance(self.fields, dict): return { key: text_type(value).format( **row) if RE_FORMATTED.match(value) else row[value] for key, value in self.fields.items() } else: row = dict(zip(self._db_fields, row)) return [ text_type(field).format( **row) if RE_FORMATTED.match(field) else row[field] for field in self.fields ] def format_data_rows(self, rows): if hasattr(self, 'format_data_row'): rows = map(self.format_data_row, rows) return rows def get_extra_data(self, extra_object_list): """Map user-defined function on extra object list. If the user has not defined a `get_extra_data_row` method, then this method has no effect. """ if hasattr(self, 'get_extra_data_row'): return map(self.get_extra_data_row, extra_object_list) def add_extra_data(self, data, extra_object_list): """Add an 'extra' dictionary to the returned JSON. By default, no extra data will be added to the returned JSON, unless the user has specified a `get_extra_data_row` method or has overriden the existing `get_extra_data` method. """ extra_data = self.get_extra_data(extra_object_list) if extra_data: data['extra'] = extra_data def render_to_response(self, form, **kwargs): '''Render Datatables expected JSON format''' if self.ServerSide and form.cleaned_data['iDisplayLength'] > 0: data_page = self.get_page(form, self.object_list) data_rows = self.get_rows(data_page.object_list) extra_object_list = self.get_page(form, self.qs).object_list data = { 'iTotalRecords': data_page.paginator.count, 'iTotalDisplayRecords': data_page.paginator.count, 'sEcho': form.cleaned_data['sEcho'], 'aaData': self.format_data_rows(data_rows), } else: data_rows = self.get_rows(self.object_list) extra_object_list = self.qs data = { 'aaData': self.format_data_rows(data_rows), } self.add_extra_data(data, extra_object_list) return self.json_response(data) def json_response(self, data): return HttpResponse(json.dumps(data, cls=DjangoJSONEncoder), mimetype=JSON_MIMETYPE)
class DatatablesViewEss(DatatablesView): def get_orders_qs(self, queryset): '''Get ordering fields for ``QuerySet.order_by``''' orders = [] iSortingCols = self.dt_data['iSortingCols'] dt_orders = [(self.dt_data['iSortCol_%s' % i], self.dt_data['sSortDir_%s' % i]) for i in xrange(iSortingCols)] for field_idx, field_dir in dt_orders: direction = '-' if field_dir == 'desc' else '' if hasattr(self, 'sort_col_qs_%s' % field_idx): method = getattr(self, 'sort_col_qs_%s' % field_idx) result, queryset = method(direction, queryset) if isinstance(result, (bytes, unicode)): orders.append(result) else: orders.extend(result) elif hasattr(self, 'sort_col_%s' % field_idx): method = getattr(self, 'sort_col_%s' % field_idx) result = method(direction) if isinstance(result, (bytes, unicode)): orders.append(result) else: orders.extend(result) else: field = self.get_field(field_idx) if RE_FORMATTED.match(field): tokens = RE_FORMATTED.findall(field) orders.extend( ['%s%s' % (direction, token) for token in tokens]) else: orders.append('%s%s' % (direction, field)) queryset = queryset.order_by(*orders) return queryset def get_queryset(self): '''Apply Datatables sort and search criterion to QuerySet''' qs = super(DatatablesView, self).get_queryset() # Perform global search qs = self.global_search(qs) # Perform column search qs = self.column_search(qs) # Perform ordered queryset qs = self.get_orders_qs(qs) return qs def process_dt_response(self, data): self.form = DatatablesForm(data) if self.form.is_valid(): #flush_transaction() self.object_list = self.get_queryset().values( *self.get_db_fields()) #print 'get_queryset: %s' % str(self.get_queryset) self.field_choices_dict = get_field_choices( self.get_queryset()[:1], self.get_db_fields()) #field_choices_dict={} #print '####################################################################################################################' #print '###object_list: %s, type: %s' % (self.object_list, type(self.object_list)) #self.object_list = get_object_list_display(self.object_list, field_choices_dict) #print '********************************************************************************************************************' #print '***object_list: %s, type: %s' % (self.object_list, type(self.object_list)) #print 'object_list: %s' % str(object_list) #print 'field_choices_dict: %s' % str(field_choices_dict) #print 'object_list_display: %s' % str(object_list_display) #self.object_list = self.get_queryset().values(*self.get_db_fields()) #self.object_list = self.object_list_display return self.render_to_response(self.form) else: return HttpResponseBadRequest() def get_page(self, form): '''Get the requested page''' page_size = form.cleaned_data['iDisplayLength'] start_index = form.cleaned_data['iDisplayStart'] if page_size == -1: #page_size = self.object_list.count() page_size = len(self.object_list) if page_size == 0: page_size = 1 paginator = Paginator(self.object_list, page_size) num_page = (start_index / page_size) + 1 return paginator.page(num_page) def can_regex(self, field): '''Test if a given field supports regex lookups''' from django.conf import settings if settings.DATABASES['default']['ENGINE'].endswith('sqlite3'): return not isinstance(get_real_field(self.model, field), UNSUPPORTED_REGEX_FIELDS) elif settings.DATABASES['default']['ENGINE'].endswith('mysql'): return not isinstance(get_real_field(self.model, field), UNSUPPORTED_REGEX_FIELDS) else: return True def global_search(self, queryset): '''Filter a queryset with global search''' search = self.dt_data['sSearch'] if search: if self.dt_data['bRegex']: criterions = [ Q(**{'%s__iregex' % field: search}) for field in self.get_db_fields() if self.can_regex(field) ] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): #criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields()) criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields() if self.can_regex(field)) search = reduce(or_, criterions) #print search queryset = queryset.filter(search) return queryset def render_to_response(self, form, **kwargs): '''Render Datatables expected JSON format''' page = self.get_page(form) #print 'page_type_object_list: %s' % type(page.object_list) page.object_list = get_object_list_display(page.object_list, self.field_choices_dict) data = { 'iTotalRecords': page.paginator.count, 'iTotalDisplayRecords': page.paginator.count, 'sEcho': form.cleaned_data['sEcho'], 'aaData': self.get_rows(page.object_list), #'aaData': self.get_rows(object_list), } return self.json_response(data)
class ClientsDatatableView(DatatablesView): model = Client fields = ("{first_name} {last_name},{pk}", "phone", "sex", "birth_date", "mail") display_fields = ("sex",) def get_company(self): return self.request.session.get("company", None) def global_search(self, queryset): return super(ClientsDatatableView, self).global_search(queryset).filter(company=self.get_company()) def column_search(self, queryset): return super(ClientsDatatableView, self).column_search(queryset).filter(company=self.get_company()) def get_queryset(self): return super(ClientsDatatableView, self).get_queryset().filter(company=self.get_company()) def get_db_fields(self): if not self._db_fields: self._db_fields = [] fields = self.fields.values() if isinstance(self.fields, dict) else self.fields for field in fields: if RE_FORMATTED.match(field): self._db_fields.extend(RE_FORMATTED.findall(field)) else: self._db_fields.append(field) return self._db_fields def process_dt_response(self, data): self.form = DatatablesForm(data) if self.form.is_valid(): # self.object_list = self.get_queryset().values(*self.get_db_fields()) self.object_list = self.display_objects(self.get_queryset().all()) return self.render_to_response(self.form) else: return HttpResponseBadRequest() def display_objects(self, objects): objects_list = [] for object in objects: o = {} for field in self.get_db_fields(): if field in self.display_fields: o[field] = getattr(object, "get_%s_display" % field)() else: o[field] = getattr(object, field) objects_list.append(o) return objects_list def get_row(self, row): """Format a single row (if necessary)""" if isinstance(self.fields, dict): return dict( [ (key, text_type(value).format(**row) if RE_FORMATTED.match(value) else row[value]) for key, value in self.fields.items() ] ) else: return [ text_type(field).format(**row) if RE_FORMATTED.match(field) else row[field] for field in self.fields ] def get_rows(self, rows): """Format all rows""" return [self.get_row(row) for row in rows]
class DatatablesDisplayFieldsMixin(object): display_fields = None def get_row(self, row): """Format a single row if necessary. :param row: The row to format. :raises: `ImproperlyConfigured` exception is class does not have a display_fields member. :returns: A list of data. """ if self.display_fields is None: raise ImproperlyConfigured( u"`DatatablesDisplayMixin` requires a display_fields tuple to" " be defined.") return [ getattr(row, name) for field, name in self.display_fields if field in self.fields ] def process_dt_response(self, data): self.form = DatatablesForm(data) if self.form.is_valid(): self.object_list = self.get_queryset() return self.render_to_response(self.form) else: return HttpResponseBadRequest() def global_search(self, queryset): """Filter a queryset using a global search. :param queryset: The queryset to filter. :returns: A filtered queryset. """ qs = copy.deepcopy(queryset) qs2 = copy.deepcopy(queryset) zero_start_term = False search = search_str = self.dt_data['sSearch'] if search: if self.dt_data['bRegex']: criterions = [ Q(**{'%s__iregex' % field: search}) for field in self.get_db_fields() if self.can_regex(field) ] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): if term.startswith(u'0'): zero_start_term = True criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields()) search = reduce(or_, criterions) queryset = queryset.filter(search) if zero_start_term: for term in search_str.split(): try: term = int(term) except ValueError: pass else: criterions = (Q(**{'%s__istartswith' % field: term}) for field in self.get_db_fields()) search = reduce(or_, criterions) qs = qs.filter(search) queryset = qs2.filter( Q(pk__in=qs.values('pk')) | Q(pk__in=queryset.values('pk'))) return queryset
class DatatablesView(MultipleObjectMixin, View): ''' Render a paginated server-side Datatables JSON view. See: http://www.datatables.net/usage/server-side ''' fields = [] _db_fields = None ServerSide = True _formatted_fields = False filters = {} def post(self, request, *args, **kwargs): return self.process_dt_response(request.POST) def get(self, request, *args, **kwargs): return self.process_dt_response(request.GET) def process_dt_response(self, data): # Switch between server-side and client-side mode. Given that # 'iColumns' is a needed server-side parameter, if it doesn't exist we # can safely switch to client-side. if 'iColumns' in data: self.generate_search_sets(data) self.form = DatatablesForm(data) if not self.form.is_valid(): return HttpResponseBadRequest() else: self.form = None self.ServerSide = False self.qs = self.get_queryset() self.set_object_list() return self.render_to_response(self.form) def set_object_list(self): if isinstance(self.fields, dict): self.object_list = self.qs.values(*self.get_db_fields()) else: self.object_list = self.qs.values_list(*self.get_db_fields()) def get_db_fields(self): if not self._db_fields: self._db_fields = [] fields = self.fields.values() if isinstance(self.fields, dict) else self.fields for field in fields: if RE_FORMATTED.match(field): self._formatted_fields = True self._db_fields.extend(RE_FORMATTED.findall(field)) else: self._db_fields.append(field) return self._db_fields @property def dt_data(self): return self.form.cleaned_data def get_field(self, index): if isinstance(self.fields, dict): return self.fields[self.dt_data['mDataProp_%s' % index]] else: return self.fields[index] def can_regex(self, field): '''Test if a given field supports regex lookups''' from django.conf import settings if settings.DATABASES['default']['ENGINE'].endswith('sqlite3'): return not isinstance(get_real_field(self.model, field), UNSUPPORTED_REGEX_FIELDS) else: return True def generate_search_sets(self, request): """Generate search sets from DataTables request. Search sets are lists of key-value pairs (in tuple format) that define the search space for the column search function and custom filter functions. These lists are generated from the DataTables HTTP request using the following method: * If the request, has an argument that starts with "sSearch_" and whose value is not empty, then we decide on which search set it will be added. * The arguments that are added in the column search set must have as suffix a number that corresponds to a table column. * All other arguments are added in the filter search set. Note: The reason why we use the values of the HTTP request instead of the values of form.cleaned_data is because we cannot always know the filter name, i.e. what is followed after "sSearch_", which means that we cannot create a form field for it. """ self.column_set = [] self.filter_set = {} for param, value in request.iteritems(): if not param.startswith("sSearch_"): continue if not value: continue _, search = param.split("_", 1) try: column_idx = int(search) if column_idx < request['iColumns']: self.column_set.append((column_idx, value),) else: self.filter_set[search] = value except ValueError: if '[]' in search: value = request.getlist(param) search = search.replace('[]', '') self.filter_set[search] = value def get_orders(self): '''Get ordering fields for ``QuerySet.order_by``''' orders = [] iSortingCols = self.dt_data['iSortingCols'] dt_orders = [(self.dt_data['iSortCol_%s' % i], self.dt_data['sSortDir_%s' % i]) for i in xrange(iSortingCols)] for field_idx, field_dir in dt_orders: direction = '-' if field_dir == DESC else '' if hasattr(self, 'sort_col_%s' % field_idx): method = getattr(self, 'sort_col_%s' % field_idx) result = method(direction) if isinstance(result, (bytes, text_type)): orders.append(result) else: orders.extend(result) else: field = self.get_field(field_idx) if RE_FORMATTED.match(field): tokens = RE_FORMATTED.findall(field) orders.extend(['%s%s' % (direction, token) for token in tokens]) else: orders.append('%s%s' % (direction, field)) return orders def global_search(self, queryset): '''Filter a queryset with global search''' search = self.dt_data['sSearch'] if search: if self.dt_data['bRegex']: criterions = [ Q(**{'%s__iregex' % field: search}) for field in self.get_db_fields() if self.can_regex(field) ] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields()) search = reduce(or_, criterions) queryset = queryset.filter(search) return queryset def column_search(self, queryset): '''Filter a queryset with column search''' for idx, search in self.column_set: if hasattr(self, 'search_col_%s' % idx): custom_search = getattr(self, 'search_col_%s' % idx) queryset = custom_search(search, queryset) else: field = self.get_field(idx) fields = RE_FORMATTED.findall(field) if RE_FORMATTED.match(field) else [field] if self.dt_data['bRegex_%s' % idx]: criterions = [Q(**{'%s__iregex' % field: search}) for field in fields if self.can_regex(field)] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in fields) search = reduce(or_, criterions) queryset = queryset.filter(search) return queryset def get_filters(self): if hasattr(self, "_filters"): return if isinstance(self.filters, list): self._filters = {filter.name: filter for filter in self.filters} # We can't check if the provided type is FilterSet, unless we try to # import it, which would not be a clean solution. Therefore, anything # that's not a list, will be stored in self._filters. else: self._filters = self.filters def custom_filtering(self, queryset): if not self.filter_set: return queryset self.get_filters() # If the following succeeds, then the user has provided as a # django-filter FilterSet and we don't actually need to iterate the # filters. try: return self._filters(data=self.filter_set, queryset=queryset).qs except TypeError: pass for attr, query in self.filter_set.iteritems(): if hasattr(self, "filter_%s" % attr): custom_filter = getattr(self, "filter_%s" % attr) queryset = custom_filter(queryset, query) else: try: f = self._filters[attr] queryset = f.filter(queryset, query) except KeyError: raise Exception('Unsupported filter: %s' % attr) return queryset def get_queryset(self): '''Apply Datatables sort and search criterion to QuerySet''' qs = super(DatatablesView, self).get_queryset() # Bail if we are not in server-side mode if not self.ServerSide: return qs # Perform global search qs = self.global_search(qs) # Perform column search qs = self.column_search(qs) # Perform custom filtering qs = self.custom_filtering(qs) # Return the ordered queryset return qs.order_by(*self.get_orders()) def get_page(self, form, object_list): '''Get the requested page''' page_size = form.cleaned_data['iDisplayLength'] start_index = form.cleaned_data['iDisplayStart'] paginator = Paginator(object_list, page_size) num_page = (start_index / page_size) + 1 return paginator.page(num_page) def get_rows(self, rows): '''Format all rows''' if self._formatted_fields: return map(self.get_row, rows) else: if isinstance(self.fields, dict): return [{key: row[value] for key, value in self.fields.iteritems()} for row in rows] else: return list(rows) def get_row(self, row): '''Format a single row (if necessary)''' if isinstance(self.fields, dict): return {key: text_type(value).format(**row) if RE_FORMATTED.match(value) else row[value] for key, value in self.fields.items()} else: row = dict(zip(self._db_fields, row)) return [text_type(field).format(**row) if RE_FORMATTED.match(field) else row[field] for field in self.fields] def format_data_rows(self, rows): if hasattr(self, 'format_data_row'): rows = map(self.format_data_row, rows) return rows def get_extra_data(self, extra_object_list): """Map user-defined function on extra object list. If the user has not defined a `get_extra_data_row` method, then this method has no effect. """ if hasattr(self, 'get_extra_data_row'): return map(self.get_extra_data_row, extra_object_list) def add_extra_data(self, data, extra_object_list): """Add an 'extra' dictionary to the returned JSON. By default, no extra data will be added to the returned JSON, unless the user has specified a `get_extra_data_row` method or has overriden the existing `get_extra_data` method. """ extra_data = self.get_extra_data(extra_object_list) if extra_data: data['extra'] = extra_data def render_to_response(self, form, **kwargs): '''Render Datatables expected JSON format''' if self.ServerSide and form.cleaned_data['iDisplayLength'] > 0: data_page = self.get_page(form, self.object_list) data_rows = self.get_rows(data_page.object_list) extra_object_list = self.get_page(form, self.qs).object_list data = { 'iTotalRecords': data_page.paginator.count, 'iTotalDisplayRecords': data_page.paginator.count, 'sEcho': form.cleaned_data['sEcho'], 'aaData': self.format_data_rows(data_rows), } else: data_rows = self.get_rows(self.object_list) extra_object_list = self.qs data = { 'aaData': self.format_data_rows(data_rows), } self.add_extra_data(data, extra_object_list) return self.json_response(data) def json_response(self, data): return HttpResponse( json.dumps(data, cls=DjangoJSONEncoder), mimetype=JSON_MIMETYPE )
class DatatablesView(MultipleObjectMixin, View): ''' Render a paginated server-side Datatables JSON view. See: http://www.datatables.net/usage/server-side ''' fields = [] _db_fields = None def post(self, request, *args, **kwargs): return self.process_dt_response(request.POST) def get(self, request, *args, **kwargs): return self.process_dt_response(request.GET) def process_dt_response(self, data): self.form = DatatablesForm(data) if self.form.is_valid(): self.object_list = self.get_queryset().values(*self.get_db_fields()) return self.render_to_response(self.form) else: return HttpResponseBadRequest() def get_db_fields(self): if not self._db_fields: self._db_fields = [] fields = self.fields.values() if isinstance(self.fields, dict) else self.fields for field in fields: if RE_FORMATTED.match(field): self._db_fields.extend(RE_FORMATTED.findall(field)) else: self._db_fields.append(field) return self._db_fields @property def dt_data(self): return self.form.cleaned_data def get_field(self, index): if isinstance(self.fields, dict): return self.fields[self.dt_data['mDataProp_%s' % index]] else: return self.fields[index] def get_orders(self): '''Get ordering fields for ``QuerySet.order_by``''' orders = [] iSortingCols = self.dt_data['iSortingCols'] dt_orders = [(self.dt_data['iSortCol_%s' % i], self.dt_data['sSortDir_%s' % i]) for i in xrange(iSortingCols)] for field_idx, field_dir in dt_orders: direction = '-' if field_dir == DESC else '' if hasattr(self, 'sort_col_%s' % field_idx): method = getattr(self, 'sort_col_%s' % field_idx) result = method(direction) if isinstance(result, (bytes, text_type)): orders.append(result) else: orders.extend(result) else: field = self.get_field(field_idx) if RE_FORMATTED.match(field): tokens = RE_FORMATTED.findall(field) orders.extend(['%s%s' % (direction, token) for token in tokens]) else: orders.append('%s%s' % (direction, field)) return orders def global_search(self, queryset): '''Filter a queryset with global search''' search = self.dt_data['sSearch'] if search: if self.dt_data['bRegex']: criterions = (Q(**{'%s__iregex' % field: search}) for field in self.get_db_fields()) search = reduce(or_, criterions) queryset = queryset.filter(search) else: ors = [] for comma_split in search.split(','): ands = [] for term in comma_split.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields()) single_term = reduce(Q.__or__, criterions) ands.append(single_term) search = reduce(Q.__and__, ands) ors.append(search) search = reduce(Q.__or__, ors) queryset = queryset.filter(search) return queryset def column_search(self, queryset): '''Filter a queryset with column search''' for idx in xrange(self.dt_data['iColumns']): search = self.dt_data['sSearch_%s' % idx] if search: if hasattr(self, 'search_col_%s' % idx): custom_search = getattr(self, 'search_col_%s' % idx) queryset = custom_search(search, queryset) else: field = self.get_field(idx) fields = RE_FORMATTED.findall(field) if RE_FORMATTED.match(field) else [field] if self.dt_data['bRegex_%s' % idx]: criterions = (Q(**{'%s__iregex' % field: search}) for field in fields) search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in fields) search = reduce(or_, criterions) queryset = queryset.filter(search) return queryset def get_queryset(self): '''Apply Datatables sort and search criterion to QuerySet''' qs = super(DatatablesView, self).get_queryset() # Perform global search qs = self.global_search(qs) # Perform column search qs = self.column_search(qs) # Return the ordered queryset return qs.order_by(*self.get_orders()) def get_page(self, form): '''Get the requested page''' page_size = form.cleaned_data['iDisplayLength'] if page_size == -1: page_size = 20 start_index = form.cleaned_data['iDisplayStart'] paginator = Paginator(self.object_list, page_size) num_page = (start_index / page_size) + 1 return paginator.page(num_page) def get_rows(self, rows): '''Format all rows''' return [self.get_row(row) for row in rows] def get_row(self, row): '''Format a single row (if necessary)''' if isinstance(self.fields, dict): return dict([ (key, text_type(value).format(**row) if RE_FORMATTED.match(value) else row[value]) for key, value in self.fields.items() ]) else: return [text_type(field).format(**row) if RE_FORMATTED.match(field) else row[field] for field in self.fields] def render_to_response(self, form, **kwargs): '''Render Datatables expected JSON format''' page = self.get_page(form) data = { 'iTotalRecords': page.paginator.count, 'iTotalDisplayRecords': page.paginator.count, 'sEcho': form.cleaned_data['sEcho'], 'aaData': self.get_rows(page.object_list), } return self.json_response(data) def json_response(self, data): return HttpResponse( json.dumps(data, cls=DjangoJSONEncoder), mimetype=JSON_MIMETYPE )
class DatatablesView(MultipleObjectMixin, View): ''' Render a paginated server-side Datatables JSON view. See: http://www.datatables.net/usage/server-side ''' fields = [] _db_fields = None def post(self, request, *args, **kwargs): return self.process_dt_response(request.POST) def get(self, request, *args, **kwargs): return self.process_dt_response(request.GET) def process_dt_response(self, data): self.form = DatatablesForm(data) if self.form.is_valid(): self.object_list = self.get_queryset().values( *self.get_db_fields()) return self.render_to_response(self.form) else: return HttpResponseBadRequest() def get_db_fields(self): if not self._db_fields: self._db_fields = [] fields = list(self.fields.values()) if isinstance( self.fields, dict) else self.fields for field in fields: if RE_FORMATTED.match(field): self._db_fields.extend(RE_FORMATTED.findall(field)) else: self._db_fields.append(field) return self._db_fields @property def dt_data(self): return self.form.cleaned_data def get_field(self, index): if isinstance(self.fields, dict): return self.fields[self.dt_data['mDataProp_%s' % index]] else: return self.fields[index] def can_regex(self, field): '''Test if a given field supports regex lookups''' from django.conf import settings if settings.DATABASES['default']['ENGINE'].endswith('sqlite3'): return not isinstance(get_real_field(self.model, field), UNSUPPORTED_REGEX_FIELDS) else: return True def get_orders(self): '''Get ordering fields for ``QuerySet.order_by``''' orders = [] iSortingCols = self.dt_data['iSortingCols'] dt_orders = [(self.dt_data['iSortCol_%s' % i], self.dt_data['sSortDir_%s' % i]) for i in range(iSortingCols)] for field_idx, field_dir in dt_orders: direction = '-' if field_dir == DESC else '' if hasattr(self, 'sort_col_%s' % field_idx): method = getattr(self, 'sort_col_%s' % field_idx) result = method(direction) if isinstance(result, (bytes, text_type)): orders.append(result) else: orders.extend(result) else: field = self.get_field(field_idx) if RE_FORMATTED.match(field): tokens = RE_FORMATTED.findall(field) orders.extend( ['%s%s' % (direction, token) for token in tokens]) else: orders.append('%s%s' % (direction, field)) return orders def global_search(self, queryset): '''Filter a queryset with global search''' search = self.dt_data['sSearch'] if search: if self.dt_data['bRegex']: criterions = [ Q(**{'%s__iregex' % field: search}) for field in self.get_db_fields() if self.can_regex(field) ] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields()) search = reduce(or_, criterions) queryset = queryset.filter(search) return queryset def column_search(self, queryset): '''Filter a queryset with column search''' for idx in range(self.dt_data['iColumns']): search = self.dt_data['sSearch_%s' % idx] if search: if hasattr(self, 'search_col_%s' % idx): custom_search = getattr(self, 'search_col_%s' % idx) queryset = custom_search(search, queryset) else: field = self.get_field(idx) fields = RE_FORMATTED.findall(field) if RE_FORMATTED.match( field) else [field] if self.dt_data['bRegex_%s' % idx]: criterions = [ Q(**{'%s__iregex' % field: search}) for field in fields if self.can_regex(field) ] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in fields) search = reduce(or_, criterions) queryset = queryset.filter(search) return queryset def get_queryset(self): '''Apply Datatables sort and search criterion to QuerySet''' qs = super(DatatablesView, self).get_queryset() # Perform global search qs = self.global_search(qs) # Perform column search qs = self.column_search(qs) # Return the ordered queryset return qs.order_by(*self.get_orders()) def get_page(self, form): '''Get the requested page''' page_size = form.cleaned_data['iDisplayLength'] start_index = form.cleaned_data['iDisplayStart'] paginator = Paginator(self.object_list, page_size) num_page = (start_index / page_size) + 1 return paginator.page(num_page) def get_rows(self, rows): '''Format all rows''' return [self.get_row(row) for row in rows] def get_row(self, row): '''Format a single row (if necessary)''' if isinstance(self.fields, dict): return dict([(key, text_type(value).format( **row) if RE_FORMATTED.match(value) else row[value]) for key, value in list(self.fields.items())]) else: return [ text_type(field).format( **row) if RE_FORMATTED.match(field) else row[field] for field in self.fields ] def render_to_response(self, form, **kwargs): '''Render Datatables expected JSON format''' page = self.get_page(form) data = { 'iTotalRecords': page.paginator.count, 'iTotalDisplayRecords': page.paginator.count, 'sEcho': form.cleaned_data['sEcho'], 'aaData': self.get_rows(page.object_list), } return self.json_response(data) def json_response(self, data): return HttpResponse(json.dumps(data, cls=DjangoJSONEncoder), mimetype=JSON_MIMETYPE)
class DatatablesView(DatatablesView): def process_dt_response(self, data): self.form = DatatablesForm(data) if self.form.is_valid(): #flush_transaction() self.object_list = self.get_queryset().values(*self.get_db_fields()) #print 'get_queryset: %s' % str(self.get_queryset) self.field_choices_dict = get_field_choices(self.get_queryset()[:1], self.get_db_fields()) #field_choices_dict={} #print '####################################################################################################################' #print '###object_list: %s, type: %s' % (self.object_list, type(self.object_list)) #self.object_list = get_object_list_display(self.object_list, field_choices_dict) #print '********************************************************************************************************************' #print '***object_list: %s, type: %s' % (self.object_list, type(self.object_list)) #print 'object_list: %s' % str(object_list) #print 'field_choices_dict: %s' % str(field_choices_dict) #print 'object_list_display: %s' % str(object_list_display) #self.object_list = self.get_queryset().values(*self.get_db_fields()) #self.object_list = self.object_list_display return self.render_to_response(self.form) else: return HttpResponseBadRequest() def get_page(self, form): '''Get the requested page''' page_size = form.cleaned_data['iDisplayLength'] start_index = form.cleaned_data['iDisplayStart'] if page_size == -1: #page_size = self.object_list.count() page_size = len(self.object_list) if page_size == 0: page_size = 1 paginator = Paginator(self.object_list, page_size) num_page = (start_index / page_size) + 1 return paginator.page(num_page) def can_regex(self, field): '''Test if a given field supports regex lookups''' from django.conf import settings if settings.DATABASES['default']['ENGINE'].endswith('sqlite3'): return not isinstance(get_real_field(self.model, field), UNSUPPORTED_REGEX_FIELDS) elif settings.DATABASES['default']['ENGINE'].endswith('mysql'): return not isinstance(get_real_field(self.model, field), UNSUPPORTED_REGEX_FIELDS) else: return True def global_search(self, queryset): '''Filter a queryset with global search''' search = self.dt_data['sSearch'] if search: if self.dt_data['bRegex']: criterions = [Q(**{'%s__iregex' % field: search}) for field in self.get_db_fields() if self.can_regex(field)] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): #criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields()) criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields() if self.can_regex(field)) search = reduce(or_, criterions) #print search queryset = queryset.filter(search) return queryset def render_to_response(self, form, **kwargs): '''Render Datatables expected JSON format''' page = self.get_page(form) #print 'page_type_object_list: %s' % type(page.object_list) page.object_list = get_object_list_display(page.object_list, self.field_choices_dict) data = { 'iTotalRecords': page.paginator.count, 'iTotalDisplayRecords': page.paginator.count, 'sEcho': form.cleaned_data['sEcho'], 'aaData': self.get_rows(page.object_list), #'aaData': self.get_rows(object_list), } return self.json_response(data)
class DatatablesView(MultipleObjectMixin, View): ''' Render a paginated server-side Datatables JSON view. See: http://www.datatables.net/usage/server-side ''' fields = [] _db_fields = None def post(self, request, *args, **kwargs): return self.process_dt_response(request.POST) def get(self, request, *args, **kwargs): return self.process_dt_response(request.GET) def process_dt_response(self, data): self.form = DatatablesForm(data) if self.form.is_valid(): self.object_list = self.get_queryset() return self.render_to_response(self.form) else: return HttpResponseBadRequest() def get_db_fields(self): if not self._db_fields: self._db_fields = [] fields = self.fields.values() if isinstance(self.fields, dict) else self.fields for field in fields: if callable(field): continue elif RE_FORMATTED.match(field): self._db_fields.extend(RE_FORMATTED.findall(field)) else: self._db_fields.append(field) return self._db_fields @property def dt_data(self): return self.form.cleaned_data def get_field(self, index): if isinstance(self.fields, dict): return self.fields[self.dt_data['mDataProp_%s' % index]] else: return self.fields[index] def can_regex(self, field): '''Test if a given field supports regex lookups''' from django.conf import settings if settings.DATABASES['default']['ENGINE'].endswith('sqlite3'): return not isinstance(get_real_field(self.model, field), UNSUPPORTED_REGEX_FIELDS) else: return True def get_orders(self): '''Get ordering fields for ``QuerySet.order_by``''' orders = [] iSortingCols = self.dt_data['iSortingCols'] dt_orders = [(self.dt_data['iSortCol_%s' % i], self.dt_data['sSortDir_%s' % i]) for i in xrange(iSortingCols)] for field_idx, field_dir in dt_orders: direction = '-' if field_dir == DESC else '' if hasattr(self, 'sort_col_%s' % field_idx): method = getattr(self, 'sort_col_%s' % field_idx) result = method(direction) if isinstance(result, (bytes, text_type)): orders.append(result) else: orders.extend(result) else: field = self.get_field(field_idx) if RE_FORMATTED.match(field): tokens = RE_FORMATTED.findall(field) orders.extend(['%s%s' % (direction, token) for token in tokens]) else: orders.append('%s%s' % (direction, field)) return orders def global_search(self, queryset): '''Filter a queryset with global search''' search = self.dt_data['sSearch'] if search: if self.dt_data['bRegex']: criterions = [ Q(**{'%s__iregex' % field: search}) for field in self.get_db_fields() if self.can_regex(field) ] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in self.get_db_fields()) search = reduce(or_, criterions) queryset = queryset.filter(search) return queryset def column_search(self, queryset): '''Filter a queryset with column search''' for idx in xrange(self.dt_data['iColumns']): search = self.dt_data['sSearch_%s' % idx] if search: if hasattr(self, 'search_col_%s' % idx): custom_search = getattr(self, 'search_col_%s' % idx) queryset = custom_search(search, queryset) else: field = self.get_field(idx) fields = RE_FORMATTED.findall(field) if RE_FORMATTED.match(field) else [field] if self.dt_data['bRegex_%s' % idx]: criterions = [Q(**{'%s__iregex' % field: search}) for field in fields if self.can_regex(field)] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): criterions = (Q(**{'%s__icontains' % field: term}) for field in fields) search = reduce(or_, criterions) queryset = queryset.filter(search) return queryset def get_queryset(self): '''Apply Datatables sort and search criterion to QuerySet''' qs = super(DatatablesView, self).get_queryset() # Perform global search qs = self.global_search(qs) # Perform column search qs = self.column_search(qs) # Return the ordered queryset return qs.order_by(*self.get_orders()) def get_page(self, form): '''Get the requested page''' page_size = form.cleaned_data['iDisplayLength'] start_index = form.cleaned_data['iDisplayStart'] paginator = Paginator(self.object_list, page_size) num_page = (start_index / page_size) + 1 return paginator.page(num_page) def get_field_value(self, row, field, value_field): if callable(value_field): return value_field(row) elif RE_FORMATTED.match(value_field): return text_type(value_field).format(**row.__dict__) elif value_field.find('__') > 0: fields = value_field.split("__") for subattr in fields: row = getattr(row, subattr) return row else: return row.__dict__.get(value_field) def get_rows(self, rows): '''Format all rows''' return [self.get_row(row) for row in rows] def get_row(self, row): '''Format a single row (if necessary)''' if isinstance(self.fields, dict): return dict([ (field, self.get_field_value(row, field, value_field)) for field, value_field in self.fields.items() ]) else: return [text_type(field).format(**row.__dict__) if RE_FORMATTED.match(field) else self.get_field_value(row, field, field) for field in self.fields] def render_to_response(self, form, **kwargs): '''Render Datatables expected JSON format''' page = self.get_page(form) data = { 'iTotalRecords': page.paginator.count, 'iTotalDisplayRecords': page.paginator.count, 'sEcho': form.cleaned_data['sEcho'], 'aaData': self.get_rows(page.object_list), } return self.json_response(data) def json_response(self, data): return HttpResponse( json.dumps(data, cls=DjangoJSONEncoder), content_type=JSON_MIMETYPE )
class DatatablesDisplayFieldsMixin(object): display_fields = None def get_row(self, row): """Format a single row if necessary. :param row: The row to format. :raises: `ImproperlyConfigured` exception is class does not have a display_fields member. :returns: A list of data. """ if self.display_fields is None: raise ImproperlyConfigured( u"`DatatablesDisplayMixin` requires a display_fields tuple to" " be defined.") return [getattr(row, name) for field, name in self.display_fields if field in self.fields] def process_dt_response(self, data): self.form = DatatablesForm(data) if self.form.is_valid(): self.object_list = self.get_queryset() return self.render_to_response(self.form) else: return HttpResponseBadRequest() def global_search(self, queryset, excludes=None): """Filter a queryset using a global search. :param queryset: The queryset to filter. :returns: A filtered queryset. """ qs = copy.deepcopy(queryset) qs2 = copy.deepcopy(queryset) zero_start_term = False search = search_str = self.dt_data['sSearch'] fields = self.get_db_fields() if excludes: for exclude in excludes: fields.remove(exclude) if exclude in fields else None if search: if self.dt_data['bRegex']: criterions = [Q(**{'%s__iregex' % field: search}) for field in fields if self.can_regex(field)] if len(criterions) > 0: search = reduce(or_, criterions) queryset = queryset.filter(search) else: for term in search.split(): if term.startswith(u'0'): zero_start_term = True criterions = (Q(**{'%s__icontains' % field: term}) for field in fields) search = reduce(or_, criterions) queryset = queryset.filter(search) if zero_start_term: for term in search_str.split(): try: term = int(term) except ValueError: pass else: criterions = (Q(**{'%s__istartswith' % field: term}) for field in fields) search = reduce(or_, criterions) qs = qs.filter(search) queryset = qs2.filter(Q(pk__in=qs.values('pk')) | Q(pk__in=queryset.values('pk'))) return queryset
class ClientsDatatableView(DatatablesView): model = Client fields = ('{first_name} {last_name},{pk}', 'phone', 'sex', 'birth_date', 'mail') display_fields = ('sex', ) def get_company(self): return self.request.session.get('company', None) def global_search(self, queryset): return super( ClientsDatatableView, self).global_search(queryset).filter(company=self.get_company()) def column_search(self, queryset): return super( ClientsDatatableView, self).column_search(queryset).filter(company=self.get_company()) def get_queryset(self): return super(ClientsDatatableView, self).get_queryset().filter(company=self.get_company()) def get_db_fields(self): if not self._db_fields: self._db_fields = [] fields = self.fields.values() if isinstance(self.fields, dict) else self.fields for field in fields: if RE_FORMATTED.match(field): self._db_fields.extend(RE_FORMATTED.findall(field)) else: self._db_fields.append(field) return self._db_fields def process_dt_response(self, data): self.form = DatatablesForm(data) if self.form.is_valid(): # self.object_list = self.get_queryset().values(*self.get_db_fields()) self.object_list = self.display_objects(self.get_queryset().all()) return self.render_to_response(self.form) else: return HttpResponseBadRequest() def display_objects(self, objects): objects_list = [] for object in objects: o = {} for field in self.get_db_fields(): if field in self.display_fields: o[field] = getattr(object, "get_%s_display" % field)() else: o[field] = getattr(object, field) objects_list.append(o) return objects_list def get_row(self, row): '''Format a single row (if necessary)''' if isinstance(self.fields, dict): return dict([(key, text_type(value).format( **row) if RE_FORMATTED.match(value) else row[value]) for key, value in self.fields.items()]) else: return [ text_type(field).format( **row) if RE_FORMATTED.match(field) else row[field] for field in self.fields ] def get_rows(self, rows): '''Format all rows''' return [self.get_row(row) for row in rows]