Typed Filters
Let granted users narrow a list by exact values and inclusive ranges on declared scalar fields.
After reading this guide, you will know:
- How to make a field filterable and which operators each type supports.
- Why filters need their own policy grant.
- How filter values are validated, combined and carried in URLs.
- What the index form and template overrides receive.
Declare Filterable Fields
For a host Order model:
class OrderAdmin < RailsAiAdmin::Resource
model "Order"
key :orders
title :number
fields do
id :id
text :number, searchable: :prefix, sortable: true
select :state, choices: [ [ "Draft", "draft" ], [ "Approved", "approved" ] ], filterable: true
decimal :total, filterable: true
date :delivery_on, filterable: %i[gte lte]
boolean :expedited, filterable: true
end
end
| Scalar type | Allowed operators | filterable: true |
|---|---|---|
id, text, textarea, enum, select, boolean |
eq |
eq |
integer, decimal, date, datetime |
eq, gte, lte |
gte, lte |
gte/lte are inclusive. Pass an Array to choose a subset, such as
filterable: %i[eq gte lte]. Text equality is exact and follows your database
collation; use searchable: for prefix or contains matching. Virtual and hidden
fields cannot be filtered. A field without filterable: keeps exactly its
previous contract.
Grant Filters Explicitly
A filter reveals values through which records match, even for a column that is not displayed. Filtering is therefore a separate field grant, denied by default and never implied by read, search or sort:
class AdminPolicyAdapter < RailsAiAdmin::Authorization::Adapter
def fields(context:, resource:, action:, record: nil)
case action
when :read then %i[id number state total delivery_on expedited]
when :search, :sort then [ :number ]
when :filter then %i[state delivery_on expedited] + (context.actor.role == "admin" ? [ :total ] : [])
else []
end
end
end
With the Pundit adapter, add filterable_fields to the resource policy. If your
adapter returns one list for every action, it grants filters as well; switch on
action instead.
Query Semantics
RailsAiAdmin::Queries::List.new(context, OrderAdmin).call(
filters: { "total" => { "gte" => "10.00", "lte" => "100" }, "expedited" => { "eq" => "false" } }
)
Conditions are combined with AND, up to 20 in total. Every value is a nonempty
string of at most 200 characters, coerced through the field’s own type: "false"
is a boolean value, decimals stay exact, dates are ISO dates and datetimes use
the authenticated Context time zone (as datetime-local inputs do). Values are
bound as parameters on the declared column; no column, operator or SQL comes
from input. An undeclared or ungranted field, an unsupported operator or a value
outside the field’s contract raises AccessDenied with INVALID_QUERY.
Filters apply inside the policy relation and any named scope, then search, sort and pagination run on the result. NULL tests, OR groups, association paths and saved filters are not supported yet.
URLs, Hints and Revocation
The index reads filter[field][operator]=value and keeps a canonical nested
filter entry in its list hint: blanks are removed and fields/operators follow
the declaration order. A submitted form with blank inputs is redirected once to
that canonical URL. Record, operation, selection and job flows carry the filter
like the other list settings, as nested return_to[filter][...] fields.
An undeclared field or operator, or a bad value for a granted field, is an invalid query (422). A condition whose grant was revoked is dropped with the “Some list settings are no longer available” notice. Filters are not part of intent, Selection or receipt identity.
Templates and Tests
resource_index (revision 4) receives filter_fields, the currently granted
filterable fields. Each operator renders a labelled input named
filter[field][operator]. Keep the hidden list-state fields in both the search
and filter forms (the list_state_fields helper does this), and use
return_navigation_fields for record forms so nested filters survive.
TestKit field snapshots include a filter group, and assert_field_grants
requires it:
RailsAiAdmin::TestKit.assert_field_grants(context: context, resource: OrderAdmin, expected: {
read: %i[id number state total delivery_on expedited], write: [], search: [ :number ],
sort: [ :number ], filter: %i[state delivery_on expedited]
})
rails_ai_admin:doctor reports filterable columns that appear in no index. That
is a review hint, not a performance verdict.