Example #1
0
 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()
Example #2
0
 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())
Example #3
0
 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())
Example #4
0
 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')
Example #5
0
 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)
Example #6
0
 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()
Example #7
0
	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')
Example #8
0
 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)
Example #9
0
 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)
Example #10
0
    def test_dyanmic_extra_parameters(self):
        '''Should dynamiclly add extra parameters'''
        form = DatatablesForm({
            'sEcho': '1',
            'iColumns': '5',
            'iDisplayStart': '0',
            'iDisplayLength': '10',
            'sSearch': '',
            'bRegex': 'false',
            'iSortingCols': '2',
        })

        for i in range(5):
            self.assertTrue('mDataProp_%s' % i in form.fields)
            self.assertTrue(
                isinstance(form['mDataProp_%s' % i].field, forms.CharField))
            self.assertFalse(form['mDataProp_%s' % i].field.required)

            self.assertTrue('sSearch_%s' % i in form.fields)
            self.assertTrue(
                isinstance(form['sSearch_%s' % i].field, forms.CharField))
            self.assertFalse(form['sSearch_%s' % i].field.required)

            self.assertTrue('bRegex_%s' % i in form.fields)
            self.assertTrue(
                isinstance(form['bRegex_%s' % i].field, forms.BooleanField))
            self.assertFalse(form['bRegex_%s' % i].field.required)

            self.assertTrue('bSearchable_%s' % i in form.fields)
            self.assertTrue(
                isinstance(form['bSearchable_%s' % i].field,
                           forms.BooleanField))
            self.assertFalse(form['bSearchable_%s' % i].field.required)

            self.assertTrue('bSortable_%s' % i in form.fields)
            self.assertTrue(
                isinstance(form['bSortable_%s' % i].field, forms.BooleanField))
            self.assertFalse(form['bSortable_%s' % i].field.required)

        for i in range(2):
            self.assertTrue('iSortCol_%s' % i in form.fields)
            self.assertTrue(
                isinstance(form['iSortCol_%s' % i].field, forms.IntegerField))
            self.assertTrue(form['iSortCol_%s' % i].field.required)

            self.assertTrue('sSortDir_%s' % i in form.fields)
            self.assertTrue(
                isinstance(form['sSortDir_%s' % i].field, forms.ChoiceField))
            self.assertTrue(form['sSortDir_%s' % i].field.required)

        self.assertFalse('iSortCol_2' in form.fields)
Example #11
0
 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()
Example #12
0
 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)
Example #13
0
 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()
Example #14
0
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)
Example #15
0
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)
Example #16
0
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
        )
Example #17
0
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)
Example #18
0
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)
Example #19
0
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]
Example #20
0
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
        )
Example #21
0
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
Example #22
0
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
        )
Example #23
0
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
Example #24
0
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]