示例#1
0
def _aquifer_qs(request):
    """
    We have a custom search which does a case insensitive substring of aquifer_name,
    exact match on aquifer_id, and also looks at an array of provided resources attachments
    of which we require one to be present if any are specified. The front-end doesn't use
    DjangoFilterBackend's querystring array syntax, preferring ?a=1,2 rather than ?a[]=1&a[]=2,
    so again we need a custom back-end implementation.

    @param request - the request object
    """
    query = request.GET
    qs = Aquifer.objects.all()
    resources__section__code = query.get("resources__section__code")
    hydraulic = query.get('hydraulically_connected')
    search = query.get('search')

    # V2 changes to `and`-ing the filters by default unless "match_any" is explicitly set to 'true'
    match_any = query.get('match_any') == 'true'
    now = timezone.now()

    # build a list of filters from qs params
    filters = []
    if hydraulic:
        filters.append(Q(subtype__code__in=serializers.HYDRAULIC_SUBTYPES))

    # ignore missing and empty string for resources__section__code qs param
    if resources__section__code:
        for code in resources__section__code.split(','):
            filters.append(Q(resources__section__code=code))

    if match_any:
        if len(filters) > 0:
            disjunction = filters.pop()

            # combine all disjunctions using `|` a.k.a. SQL `OR`
            for filter in filters:
                disjunction |= filter
            qs = qs.filter(disjunction)
    else:
        # calling .filter() one after another combines `Q`s using SQL `AND`
        for filter in filters:
            qs = qs.filter(filter)

    if search:  # only search if the search query is set to something
        disjunction = Q(aquifer_name__icontains=search)
        # if a number is searched, assume it could be an Aquifer ID.
        if search.isdigit():
            disjunction = disjunction | Q(pk=int(search))
        qs = qs.filter(disjunction)

    # filter out any non-published aquifer if the user doesn't have the `aquifers_edit` perm
    if not request.user.groups.filter(name=AQUIFERS_EDIT_ROLE).exists():
        qs = qs.filter(effective_date__lte=now, expiry_date__gt=now)

    qs = qs.select_related('demand', 'material', 'productivity', 'subtype',
                           'vulnerability')

    qs = qs.distinct()

    return qs
示例#2
0
文件: filters.py 项目: predicthq/rfhq
    def get_filters(self, request, view):
        valid_params = self.get_valid_params(view)
        params = {self.map_param_name(view, k): self.parse_param_value(view, k, v) for k, v in request.query_params.items() if k in valid_params}
        filters = []

        for field, values in params.items():
            for config in values:
                filters.append(F("geo_distance", **{field: config[1], 'distance': config[0]}))
        return filters
示例#3
0
文件: filters.py 项目: predicthq/rfhq
 def get_filters(self, filter_params):
     filters = []
     for field, params in filter_params.items():
         negate = params.pop("negate", False)
         if self.value_operators and field not in params:
             raise ParseError("No value to match for '{}'".format(field))
         f = self.build_filter(field, **params)
         filters.append(~f if negate else f)
     return filters
示例#4
0
文件: filters.py 项目: predicthq/rfhq
 def get_filters(self, request, view):
     params = self.get_params(request, view)
     options = self.get_options(view)
     filters = []
     for param, value in params.iteritems():
         field_config = options.get(param)
         for id in value.split(','):
             filter_config = deepcopy(field_config['config'])
             field = field_config.get('field') if 'field' in field_config else param
             cache = field_config.get('cache') if 'cache' in field_config else False
             filter_config['indexed_shape']['id'] = id
             filters.append(F("geo_shape", **{field: filter_config, '_cache': cache}))
     return filters
示例#5
0
文件: filters.py 项目: predicthq/rfhq
 def filter_queryset(self, request, queryset, view):
     bins = self.get_options(view)
     filters = []
     for bin_name, bin_options in bins.items():
         if bin_name in request.query_params:
             options = [v.strip() for v in request.query_params.get(bin_name).split(',')]
             for o in options:
                 try:
                     filters.append(F("range", **bin_options[o]))
                 except KeyError:
                     raise ParseError("Invalid value '{}' for '{}'".format(o, bin_name))
     if filters:
         return queryset.filter("bool", must=[F("bool", should=filters)])
     return queryset
    def build_filter(self, fields, filter_value_raw):
        filters = []
        filter_parser = parser.build_filter_parser(fields.keys())

        def require_text_fields(parser_fields, operator_name):
            for pf in parser_fields:
                if type(fields[pf.name]) not in (model_fields.TextField,
                                                 model_fields.CharField):
                    raise BadQuery(
                        "The operator \"{0}\" is only allowed with text fields"
                        .format(operator_name))

        join_op = parser.LogicalOp('and')
        for q in filter_parser.parseString(filter_value_raw,
                                           parseAll=True).asList():
            if isinstance(q, parser.Comparison):
                q_fields = q.fields
                left = q_fields[0]
                op = q.operator

                right = None
                if len(q_fields) > 1:
                    right = F(q_fields[1].name)
                else:
                    if len(q.values) != 0:
                        right = self._value_cast(fields[left.name],
                                                 q.values[0].value)

                # find the matching operator in djangos ORM syntax
                model_op = None
                negate = False
                if op.op == "=":
                    model_op = "exact"
                elif op.op == "!=":
                    model_op = "exact"
                    negate = True
                elif op.op in (">", "gt"):
                    model_op = "gt"
                elif op.op in (">=", "gte"):
                    model_op = "gte"
                elif op.op in ("<", "lt"):
                    model_op = "lt"
                elif op.op in ("<=", "lte"):
                    model_op = "lte"
                elif op.op == "eq":
                    negate = op.negate
                    model_op = "exact"
                elif op.op == 'contains':
                    negate = op.negate
                    require_text_fields(q_fields, 'contains')
                    model_op = 'contains'
                elif op.op == 'icontains':
                    negate = op.negate
                    require_text_fields(q_fields, 'icontains')
                    model_op = 'icontains'
                elif op.op == 'startswith':
                    negate = op.negate
                    require_text_fields(q_fields, 'startswith')
                    model_op = 'startswith'
                elif op.op == 'istartswith':
                    negate = op.negate
                    require_text_fields(q_fields, 'istartswith')
                    model_op = 'istartswith'
                elif op.op == 'endswith':
                    negate = op.negate
                    require_text_fields(q_fields, 'endswith')
                    model_op = 'endswith'
                elif op.op == 'iendswith':
                    negate = op.negate
                    require_text_fields(q_fields, 'iendswith')
                    model_op = 'iendswith'
                elif op.op == 'isnull':
                    negate = op.negate
                    model_op = 'isnull'
                    right = True  # negation happens using ~
                else:
                    raise BadQuery("Unsupported operator: \"{0}\"".format(
                        op.op))

                f = Q(**{"{0}__{1}".format(left.name, model_op): right})
                if negate:
                    f = ~f

                # add the new filter to the existing filterset
                # "or" has precedence over "and"
                if join_op.op == 'or':
                    filters[-1] = filters[-1] | f
                elif join_op.op == 'and':
                    filters.append(f)
                else:
                    raise BadQuery(
                        "Unsupported logical operator \"{0}\"".format(
                            join_op.op))
            elif isinstance(q, parser.LogicalOp):
                join_op = q
            else:
                raise BadQuery("Unsupported element: \"{0}\"".format(type(q)))
        return filters