On an ActiveAdmin page, filters can help make information more usable. But sometimes, we need to add filters that are more complex than normal, going beyond a simple 'where' clause applied to only one column of the table.
To achieve this, we'll start by defining the filter like this:
# app/admin/my_model.rb
filter :my_custom_filter,
as: :numeric,
label: 'Custom Filter',
filters: [:eq]In the model, we'll then define a scope or class method that returns an Active Record Relation:
# app/models/my_model.rb
def self.my_custom_filter_eq(value)
where(column_1: value) # or probably a more complex query that uses the value inputted by the admin user
endAnd finally, we'll need to specify that the scope is ransackable. This means we'll add the following to the model:
# app/models/my_model.rb
def self.ransackable_scopes(_auth_object = nil)
%i(my_custom_filter_eq)
endThis example assumes we want to create a filter that receives a numeric value as a parameter, and for simplicity, we've limited the querying options to equality. But that's just one possibility – you can play around with other predicates and change param types to see what's most useful in your case.
It's important to mention that the filter has to match the scope or class method name and that it's not recommended to end it with any predicates used by ransack, like "eq", "contains", etc. This is because you could end up pointing the filter to a ransack generated scope.
As an example, let's add a filter that uses a date range for a custom column.
Suppose your model has a 'belongs to' relation with the user model, and the user has an activation date. Now, you want to filter the records belonging to a user that was activated between two dates.
Here's what goes in the admin model:
# app/admin/my_model.rb
filter :activated_in_period,
as: :date_range,
label: 'Of user activated during period'And in the model:
# app/models/my_model.rb
def self.ransackable_scopes(_auth_object = nil)
%i[activated_in_period_gteq_datetime activated_in_period_lteq_datetime]
end
def self.activated_in_period_gteq_datetime(date_from)
joins(:user).where('users.activation_date >= ?', date_from)
end
def self.activated_in_period_lteq_datetime(date_to)
joins(:user).where('users.activation_date <= ?', date_from)
endAnd that's it! We've only used a couple of examples here, but this code structure is the foundation for many other types of custom filters you may want to create, so you can tweak it to achieve a variety of goals.