ajax_slickgrid.py 4.64 KB
# -*- coding: utf-8 -*-


class SlickGridQuery(object):
    model = None
    sort_field = None
    filter_field_translation = {}
    select_related = []
    default_columns = ()
    column_data = {}

    lookup_translation = {
        'eq': 'exact',
        'ne': '-exact',
        'bw': 'startswith',
        'bn': '-startswith',
        'ew': 'endswith',
        'en': '-endswith',
        'cn': 'contains',
        'nc': '-contains',
        're': 'regex',
        'nr': '-regex',
        'le': 'lte',
        'ge': 'gte',
        'in': 'in',
        '-in': '-in',
        'isnull': 'isnull',
        '-isnull': '-isnull',
    }

    def __init__(self, query_params, columns=None):
        self.query_params = query_params
        self.filter = query_params['filter']
        self.sort_rules = query_params['sort_rules']
        if columns:
            self.columns = tuple(columns)
        else:
            self.columns = self.default_columns

    def get_queryset(self):
        return self.model.objects.all()

    def get_empty_queryset(self):
        return self.model.objects.none()

    def translate_filter_field(self, field):
        return self.filter_field_translation.get(field, field)

    def apply_filter_rule(self, queryset, rule):
        lookup = self.lookup_translation[rule['op']]
        negated = (lookup[0] == '-')
        lookup = lookup.lstrip('-')
        field = self.translate_filter_field(rule['field'])
        data = rule['data']
        if not data and lookup == 'in':
            lookup = 'isnull'
            data = not negated
            negated = False
        arg = {(field + '__' + lookup): data}
        if negated:
            queryset = queryset.exclude(**arg).distinct()
        else:
            queryset = queryset.filter(**arg).distinct()
        return queryset

    def apply_filter(self):
        queryset = self.get_queryset()
        if self.filter:
            if self.filter['group_op'] == 'AND':
                for rule in self.filter['rules']:
                    queryset = self.apply_filter_rule(queryset, rule)
            elif self.filter['group_op'] == 'OR':
                new_queryset = self.get_empty_queryset()
                for rule in self.filter['rules']:
                    new_queryset |= self.apply_filter_rule(queryset, rule)
                queryset = new_queryset
        return queryset

    def sort_queryset(self, queryset):
        order_list = [self.sort_field]
        return queryset.extra(order_by=order_list)

    def filter_from(self, queryset, from_value, upward):
        lookup = '__gte' if upward else '__lte'
        return queryset.filter(**{self.sort_field + lookup: from_value})

    # indeks wiersza w danym sortowaniu, w którym
    # znajdzie się rekord o danym id
    def row_index(self, record_id):
        selected = self.model.objects.get(pk=record_id)
        queryset = self.apply_filter()
        if selected not in queryset:
            return None
        data = getattr(selected, self.sort_field)
        preceding = self.filter_from(
            queryset, from_value=data, upward=False)
        return max(preceding.count() - 1, 0)

    def search_index(self, mask):
        queryset = self.apply_filter()
        count = queryset.count()
        if count > 0 and mask == '':
            return 0
        if queryset.count() > 0:
            whole_queryset = queryset
            queryset = self.filter_from(
                queryset, from_value=mask, upward=True)
            if queryset.count() == 0:
                queryset = whole_queryset
            queryset = self.sort_queryset(queryset)
            index = self.row_index(queryset[0].id)
        else:
            index = 0
        return index

    def get_sorted_queryset(self):
        queryset = self.apply_filter()
        return self.sort_queryset(queryset)

    @staticmethod
    def count_pages(from_page, to_page, limit):
        start = limit * from_page
        pages = to_page - from_page + 1
        response_rowcount = limit * pages
        return start, response_rowcount

    def response_row(self, record):
        return dict((column, self.column_data[column](record))
                    for column in ('id', 'scheme') + self.columns)

    def prepare_rows(self, records):
        return [self.response_row(record) for record in records]

    def get_page(self, from_page, to_page, limit):
        queryset = self.get_sorted_queryset()
        if self.select_related:
            queryset = queryset.select_related(*self.select_related)
        count = queryset.count()
        start, response_rowcount = self.count_pages(from_page, to_page, limit)
        records = queryset[start:start + response_rowcount]
        return self.prepare_rows(records), count