API reference

class filternaut.FilterTree(negate=False, *args, **kwargs)

FilterTrees instances are the result of ORing or ANDing Filter instances, or other FilterTree instances, together.

FilterTree provides a simple API for dealing with a set of filters. They may also be iterated over to gain access to the Filter instances directly.

parse(data)

Have this tree of filters convert data into a Django Q-object.

class filternaut.Filter(dest, **kwargs)

A Filter instance builds a django.db.models.Q object by pulling a value from arbitrary native data, e.g. a set of query params.

It can be ORed or ANDed together with other Filter instances in the same way Q objects can.

clean(value)

Validate and normalise value for use in filtering. This implementation is a no-op; subclasses may do more work here.

parse(data)

Return Q-object which can be used with Django ORM.

In the general case this is just Q() wrapped around the output of Filter.parse_to_dict(); they are different representations of the same information:

parse_to_dict() -> {"id": 1}
parse()         -> Q(id=1)

When none-to-isnull conversion is enabled and relevant, this method returns a query which differs slightly from the dict representation:

parse_to_dict() -> {"id__in": [1, 2, None]}
parse()         -> Q(id__in=[1, 2]) | Q(id__isnull=True)
parse_to_dict(data)

Look through the provided dict-like data for keys which match this Filter’s source. This includes keys containing lookup affixes such as ‘contains’ or ‘lte’:

{"created_date__gte": "2020-01-01..."}

If you just want to plug Filternaut into Django’s ORM, call Filter.parse() instead.

If you can’t get Filternaut to do what you want, you might have an easier time post-processing the output of parse_to_dict and dropping it directly into the ORM’s filter() yourself. Note that this method is only available on individual filters and not on a combination (such as Filter("a") & Filter("b")), so this approach only takes you so far.

tree_class

Filters combine into FilterTree instances

alias of FilterTree

class filternaut.Constraint(left, *rest)

Base class for making collective rules over several filters.

For example, at least two of the filters must be used, or only one of the filters may be used, etc.

This class book-keeps which filters were used as it parses the incoming data. Subclasses can use this to decide whether to add or remove errors.

class FilterUse(filter, valid, missing)

Subclasses will receive a FilterUse for each child filter. This should be used to adjust validation as necessary. See Constraint.apply_constraint().

filter

Alias for field number 0

missing

Alias for field number 2

valid

Alias for field number 1

apply_constraint(report, errors)

Subclasses should examine how filters were used in report and mutate errors as necessary.

class filternaut.Optional(left, *rest)

Filters included underneath Optional have their required=True configuration ignored as long as all those filters are missing. If some but not all are present, then required=True is observed, and those filters that are missing become invalid. If all filters under Optional have valid source data, they are valid as a whole.

This is useful for situations where you want to require one field if another is present. For example, requiring last_name if first_name is present, but also allowing neither. In this case, you would mark both with required=True, and wrap them in Optional:

Optional(
    Filter('first_name', required=True),
    Filter('last_name', required=True),
)

Generally, filters underneath Optional will have required=True, however it isn’t necessary; consider adding ‘middle name’ to the above example.

apply_constraint(report, errors)

Subclasses should examine how filters were used in report and mutate errors as necessary.

class filternaut.OneOf(left, *rest)

Only one of the child filters can be specified at a time. Specifying two or more causes a validation error. Use like so:

filters = OneOf(
    Filter("foo"),
    Filter("bar"),
    Filter("baz"),
)
apply_constraint(report, errors)

Subclasses should examine how filters were used in report and mutate errors as necessary.

class filternaut.filters.FieldFilter(dest, field, **kwargs)

FieldFilters use a django.forms.Field to clean their input value when generating a Q object.

This class is designed to be extended by subclasses which provide their own form-field instances. However, you could use it in combination with a custom field like so:

filter = FieldFilter(SpecialField(), dest='...')
clean(value)

Validate and normalise value for use in filtering. This implementation is a no-op; subclasses may do more work here.

class filternaut.drf.FilternautBackend

FilternautBackend is a “custom generic filtering backend” for Django REST framework: http://www.django-rest-framework.org/api-guide/filtering/#custom-generic-filtering

It allows straightforward filtering of a view’s queryset using request parameters.

filter_attr = 'filternaut_filters'

The host view must define filters at this attribute.

filter_queryset(request, queryset, view)

Decide whether to apply the filters defined by view.filternaut_filters on the argued queryset. If the filters parse correctly, is_valid is called. If not, is_invalid is called

is_invalid(request, queryset, errors)

Raise a ParseError containing the filter errors. This results in a 400 Bad Request whose body details those errors. Provided for convenience when subclassing.

is_valid(request, queryset, query)

Apply Q-object query to queryset. Provided for convenience when subclassing.

class filternaut.filters.DateTimeFilter(dest, **kwargs)