Queries¶
How do I override what operator is used for a query?¶
The member query_operator_to_q_operator for Filter is used to convert from e.g. :
to icontains. You can specify another callable here:
Table(
auto__model=Track,
columns__album__filter__query_operator_to_q_operator=lambda op: 'exact',
)
The above will force the album name to always be looked up with case
sensitive match even if the user types album<Paranoid in the
advanced query language. Use this feature with caution!
See also How do I control what Q is produced?
How do I control what Q is produced?¶
For more advanced customization you can use value_to_q. It is a
callable that takes filter, op, value_string_or_f and returns a
Q object. The default handles __, different operators, negation
and special handling of when the user searches for null.
class AlbumTable(Table):
class Meta:
auto__model = Album
query__form__fields__eighties = Field.boolean(
display_name="the '80s",
)
@staticmethod
def query__filters__eighties__value_to_q(value_string_or_f, **_):
if value_string_or_f == "1":
return Q(year__gte=1980) & Q(year__lte=1989)
return Q()
Without the filter selected:
How do I control the name used in the advanced query?¶
By default the names of filters are derived from the name you specify or from the model field name.
For deeply nested names, double underscores are replaced with single underscores, and those names
can become a bit unwieldy. You can then override this with query_name:
track_table = Table(
auto__model=Track,
auto__include=['name', 'album', 'album__artist__name'],
columns__album_artist_name__filter=dict(
include=True,
query_name='artist',
),
)
How do I filter on the thing itself?¶
Filtering a table on the thing itself is sometimes useful, but can be a bit unintuitive:
albums = Table(
auto__model=Album,
auto__include=['name', 'artist', 'year'],
query__filters__album=Filter.choice_queryset(
attr=None,
choices=Album.objects.all(),
value_to_q=lambda value_string_or_f, **_: Q(pk=value_string_or_f) if value_string_or_f else Q(),
),
)
How do I style the query container?¶
The Query component renders a container element that wraps the filter form.
You can configure its tag and attributes directly. The simple (GUI) form itself
is wrapped in form_container, which you can configure the same way:
albums = Table(
auto__model=Album,
auto__include=['name', 'artist', 'year'],
columns__name__filter__include=True,
query__attrs__style__border='2px solid red',
)
How does a query filter the rows, and how do I post-process them?¶
A Query holds a set of filters and the rows (and model) it filters. When
bound it builds a Q from the request, and its filter method applies that to
the rows. Use postprocess to operate on the resulting rows afterwards. A
Query is usually created for you by a Table, but you can declare and use one
on its own:
class AlbumQuery(Query):
name = Filter()
year = Filter.integer()
query = AlbumQuery(rows=Album.objects.all())
How do I choose which filters are generated from a model?¶
Like tables and forms, a Query can introspect a model. Pass auto__model (or
auto__rows) and pick the filters with auto__include/auto__exclude, or flip
the default with auto__default_included:
query = Query(
auto__model=Album,
auto__exclude=['year'],
)
How do I configure a filter’s GUI field?¶
Each filter has a GUI form field, configured through the filter’s field
namespace. For choice filters choices is the list of options, and
search_fields controls which model fields the autocomplete of a
choice_queryset filter searches:
albums = Table(
auto__model=Album,
columns__year__filter=dict(
include=True,
field__include=True,
),
columns__artist__filter=dict(
include=True,
search_fields=['name'],
),
)
How do I customize how a single filter matches?¶
Lower-level hooks change how an individual filter behaves:
query_operator_for_fieldis the operator used for the simple (GUI) form, e.g.=for exact-ish matching or:for “contains”.parseparses the user’s input string into a value.unarymarks a filter as usable without a value in the advanced query language.is_valid_filterdecides whether a filter is allowed to be part of a query at all.pk_lookup_to_qcontrols how a lookup by primary key is turned into aQ.
Filters generated from a model also carry model, model_field and
model_field_name (the same introspection you get on columns and fields), so a
custom hook can read the Django field’s metadata when it needs to.
albums = Table(
auto__model=Album,
columns__name__filter=dict(
include=True,
query_operator_for_field=':',
),
)
How do I customize the advanced query language area?¶
A Query with filters offers an “advanced” free-text query language alongside
the GUI form, with a link to toggle between them. Configure that toggle (and the
advanced area) through the advanced namespace:
albums = Table(
auto__model=Album,
columns__name__filter__include=True,
query__advanced__toggle__attrs__class__advanced_toggle=True,
)
How do I let a filter match several values at once?¶
A filter on a foreign key (the related shortcut, which is what you get by
default for an album or artist column) normally lets the user pick a
single value in the GUI. Pass multi_select=True to turn it into a
multi-select where the chosen values are OR:ed together:
tracks = Table(
auto__model=Track,
auto__include=['name', 'album'],
columns__album__filter__include=True,
columns__album__filter__multi_select=True,
)
Selecting two albums now produces a query like album__pk=1 OR album__pk=2
instead of forcing the user to choose just one.