Flask-Admin: route to models view with filter applied
Asked Answered
S

3

6

I have a model in Flask-Admin with filter (e.g. based on Foreign Key to other model).

I want to generate links from front-end to this model view in admin with filter value applied. I noticed that it adds ?flt0_0= to the url, so that the whole address looks kinda:

http:/.../admin/model_view_<my model>/?flt0_0=<filter value>

Which is the best way to generate routes like this?

Succubus answered 6/3, 2016 at 21:53 Comment(0)
T
5

Unfortunately, there's no public API for this yet. Here's a short snippet you can use for now to generate fltX_Y query string:

class MyView(BaseModelView):
...
    def get_filter_arg(self, filter_name, filter_op='equals'):
        filters = self._filter_groups[filter_name].filters
        position = self._filter_groups.keys().index(filter_name)

        for f in filters:
            if f['operation'] == filter_op:
                return 'flt%d_%d' % (position, f['index'])

Then you can call this method on a your view instance:

print my_view.get_filter_arg('Name', 'contains')
Teferi answered 7/3, 2016 at 15:46 Comment(0)
T
8

Flask-Admin defaults to the flt0_0=<value> syntax to be "robust across translations" if your app needs to support multiple languages. If you don't need to worry about translations, setting named_filter_urls=True is the way to go.

With named_filter_urls=True Flask-Admin generates filter query parameters like:

flt0_country_contains=<value>

The remaining integer after flt (0 in this case) is a sort key used to control the order of the filters as they appear in the UI when you have multiple filters defined. This number does not matter at all if you have a single filter.

For example, in my app I have named filters turned on. If I have multiple filters without the sort key the filters are displayed in the order they appear in the query string:

?flt_balance_smaller_than=100&flt_balance_greater_than=5

Yields: Default filter ordering

With a sort key added to the flt parameters, then I can force those filters to be displayed in a different order (flt1 will come before flt2):

?flt2_balance_smaller_than=100&flt1_balance_greater_than=5

Yields: Forced filter ordering

In practice it looks like this sort key can be any single character, e.g. this works too:

?fltB_balance_smaller_than=100&fltA_balance_greater_than=5

This behavior is ultimately defined in the Flask-Admin BaseModelView._get_list_filter_args() method here: https://github.com/flask-admin/flask-admin/blob/master/flask_admin/model/base.py#L1714-L1739

Trafalgar answered 31/7, 2019 at 19:25 Comment(0)
A
7

I prefer setting named_filter_urls=True on my base view to get rid of these magic numbers (though you can just set it on any specific view as well):

class MyBaseView(BaseModelView):
    ...
    named_filter_urls = True


class MyView(MyBaseView):
    ...
    column_filters = ['name', 'country']

This creates URLs like: http://.../admin/model/?flt_name_equals=foo&flt_country_contains=bar (*)

With this, your URLs can easily be contructed using the name of the attribute you want to filter on. As a bonus, you don't need to have a view instance available - important if you want to link to a view for a different model.

*(When selecting filters from UI, Flask-Admin will insert integers into the parameter keys. I'm not sure why it does that, but they don't appear necessary for simple filtering.)

Antiphonal answered 22/1, 2018 at 11:53 Comment(1)
I wouldn't be sure about using it, it causes a lot of unpredictable behaviour while using multiple filtersNorthington
T
5

Unfortunately, there's no public API for this yet. Here's a short snippet you can use for now to generate fltX_Y query string:

class MyView(BaseModelView):
...
    def get_filter_arg(self, filter_name, filter_op='equals'):
        filters = self._filter_groups[filter_name].filters
        position = self._filter_groups.keys().index(filter_name)

        for f in filters:
            if f['operation'] == filter_op:
                return 'flt%d_%d' % (position, f['index'])

Then you can call this method on a your view instance:

print my_view.get_filter_arg('Name', 'contains')
Teferi answered 7/3, 2016 at 15:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.