Skip to main content
iVentureTeam

filterable_selection

One selection field, different choices per view: filterable_selection filters a selection's dropdown with a whitelist, a blacklist, or a list read live from another field on the record, without touching the model.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 19, 2026Updated August 19, 20266 min read
Odoo 19 form showing a filterable_selection dropdown where blacklisted selection values are hidden from the available choices.
Technical namefilterable_selection
Field typesselection, many2one (inherited from the selection field)
Viewsform, list
Moduleweb, present in every Odoo database
Used in core5 occurrences across 4 modules, including hr, loyalty, hr_holidays_attendance, account_edi_ubl_cii
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0
No-code setupNo: the filter options are XML only
Alternativesselection, dynamic_selection, selection_badge

What the Filterable Selection widget does

A selection field's values are defined once, on the model. But views often need less than the full list: the loyalty program form should not offer Gift Card and eWallet (those have their own menus), a payroll view should only offer the states that make sense in its context. Redefining the field per view is not possible; filtering the dropdown is, and that is precisely what filterable_selection does.

It extends the standard selection widget and overrides one getter, options, applying whichever filter option you passed. Everything else, the dropdown rendering, keyboard behavior, the kanban autosave the base widget applies, is inherited unchanged.

The subtle design decision in the source: whatever the filter says, the value currently stored on the record is always included. A record whose value was set before the filter existed, or set from another view, still displays correctly instead of rendering an empty dropdown, and the user can see what they are changing away from.

What this means for your team

This widget solves a governance problem: one model serving several audiences.

Say your quality states are draft, internal review, customer review, approved and obsolete. The operator view should only let people move between draft and internal review; managers see everything. Same field, two views, two filters, zero Python. That is cheaper to build and to upgrade than duplicated fields or onchange guards, and the model's data stays uniform for reporting.

The whitelist_fname variant goes further: the allowed values can be computed per record. A program in use (the loyalty example: coupon_count != 0 makes the field read-only, and the blacklist trims the choices) or a workflow whose next legal states depend on the current one can drive the dropdown from a computed field. That is a state machine enforced in the UI for the price of one compute method.

The honest caveat: this is UI-level filtering only. Records can still receive excluded values through imports, RPC or other views, so anything compliance-critical needs a Python constraint as well.

Supported options in Odoo 19

Verified against filterable_selection_field.js in the Odoo 19.0 web module. All three options are declared in supportedOptions and forwarded verbatim by extractProps. Note the declared type is "string" for all three, but the component's props expect real arrays for the two value lists; in practice you pass Python-style lists in the options attribute, as core does.

OptionTypeWhat it does
whitelisted_valueslist of selection keysOnly these values are offered in the dropdown, plus the record's current value if it falls outside the list. Ignored when whitelist_fname is set.
blacklisted_valueslist of selection keysThese values are removed from the dropdown; everything else stays. Lowest precedence: ignored when either whitelist option is set.
whitelist_fnamefield nameNames a field on the current record whose value is the list of allowed keys, letting the filter change per record. Highest precedence of the three. The field must be loaded in the view; an empty or missing value offers only the current value.(since Odoo 18.0)

The three options do not combine. The source checks them in an if/else-if chain: if whitelist_fname is set the other two are ignored, and whitelisted_values shadows blacklisted_values. Pick exactly one strategy per view. And in every case the record's current value stays selectable-looking in the dropdown, by design.

Working examples

Blacklist, as the loyalty app uses it

<field name="program_type" widget="filterable_selection"
       options="{'blacklisted_values': ['gift_card', 'ewallet']}"/>

Every program type except the two blacklisted ones is offered.

Whitelist for a restricted view

<field name="state" widget="filterable_selection"
       options="{'whitelisted_values': ['draft', 'internal_review']}"/>

Field-driven filter (Odoo 18+)

<field name="allowed_states" column_invisible="1"/>
<field name="state" widget="filterable_selection"
       options="{'whitelist_fname': 'allowed_states'}"/>

With a compute on the model:

allowed_states = fields.Json(compute="_compute_allowed_states")

def _compute_allowed_states(self):
    for rec in self:
        rec.allowed_states = ["draft", "internal_review"] if rec.is_operator_ctx else [s[0] for s in rec._fields["state"].selection]

The referenced field must be loaded in the view; invisible is fine.

From loyalty hack to core widget

The widget's history is a textbook example of how Odoo promotes patterns from app to core.

In Odoo 16 it lived inside the loyalty module, built for exactly one job: keeping Gift Card and eWallet out of the program type dropdown. Odoo 17 moved the file into web core unchanged, whitelist and blacklist only. Odoo 18 added whitelist_fname, the record-driven variant, and the 18 and 19 files are byte-identical. If you maintained a custom "restricted selection" widget, and most integrators have written one, every Odoo since 17 ships it natively.

Two inherited details from the base selection widget carry over and are easy to miss. First, in kanban views the base widget hardcodes autosave, so changing a filtered value on a kanban card saves the record immediately. Second, on many2one fields the base widget loads choices with a single cached name_search capped at the ORM's default 100 records, and the filter then applies on top of that cache; on large models the whitelist can silently miss records beyond the cap, so keep this widget on selection fields or small relations.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Functionally unchanged on the development branch; internal props-schema rework only.
Odoo 19.0VerifiedVerified against the shipped source; byte-identical to 18.0.
Odoo 18.0VerifiedAdds whitelist_fname.
Odoo 17.0Partial / changedIn web core with whitelisted_values and blacklisted_values only.
Odoo 16.0Partial / changedExists inside the loyalty module only, with whitelist and blacklist; requires loyalty installed.

Upgrade note. Views written for 16 must change the import context: the widget name is the same, but in 16 it only exists with the loyalty app installed, while 17+ ships it in web core. whitelist_fname in views deployed on 17 will be silently ignored, so gate that option on 18+.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. The notes below are read from the public development branch and are not final until release.

No functional changes: the filter logic, all three options and their precedence are unchanged on master. The diff is the framework-wide migration to the new props-schema API (useProps with typed validators), which only affects JavaScript that spreads this component's props. We re-verify against the shipped release.

Common problems and fixes

SymptomCause and fix
A value you excluded still shows in the dropdownIt is the record's current value; the widget always keeps it visible so existing data renders. That is by design. Migrate the stored values if the choice should truly disappear.
Combining a whitelist and a blacklist only applies one of themThe options are checked in an if/else-if chain: whitelist_fname, then whitelisted_values, then blacklisted_values. Use a single strategy; express the combined rule as one whitelist.
whitelist_fname seems to be ignoredThe referenced field is not loaded in the view, or you are on Odoo 17 where the option does not exist. Add the field to the view (invisible works) and confirm the version is 18 or later.
Excluded values keep appearing in the data anywayThe filter is UI-only; imports, RPC calls and other views can still write any selection value. Back the rule with a Python constraint or selection restriction on the model.
Changing the value on a kanban card saves the whole record instantlyInherited from the base selection widget, which hardcodes autosave in kanban views. Expected behavior; edit from the form view when you want staged changes.

Filterable Selection widget vs the alternatives

WidgetBest forKey difference
filterable_selectionOne selection field offering different choices per view or recordFilters the dropdown with whitelist, blacklist or a record-driven list; current value always survives
selectionThe plain dropdown with every defined valueNo filtering; the base widget this one extends
dynamic_selectionChoice lists maintained as data rather than codeValues come from records, not from filtering a static selection
selection_badgeShowing all values as clickable badgesDisplays the full set for one-click switching instead of hiding any

Use this widget when the choices must vary by view or record. If the values themselves live in data rather than code, dynamic_selection is the fit; if you want all values visible but styled, selection_badge or radio show the full set at once.

Frequently asked questions

How do I hide some selection values in one Odoo view only?+
Use widget="filterable_selection" with options="{'blacklisted_values': [...]}" or a whitelist. The field definition on the model stays untouched, so other views and reports still see every value.
Can the allowed values depend on the record?+
Yes, since Odoo 18: set whitelist_fname to a field on the record that holds the allowed keys, typically a computed JSON field. The dropdown then updates per record.
Why is the current value shown even though I blacklisted it?+
The source always includes the record's stored value so existing data never displays blank. To remove it from records too, migrate the data; the widget will not do that for you.
Does filterable_selection prevent bad values from being saved?+
Only through this specific dropdown. Imports, API writes and other views bypass it entirely, so enforce anything critical with a Python constraint as well.
Which option wins if I set several?+
Strict precedence from the source: whitelist_fname first, then whitelisted_values, then blacklisted_values. The others are ignored, not merged.
Where does core Odoo use this widget?+
Five places across four modules; the clearest example is the loyalty program form, where the program type dropdown blacklists gift_card and ewallet because those program kinds are managed from their own menus.

Same Odoo, different rules per team?

Operators who see three states, managers who see five, and a model that stays clean underneath: that is view engineering, and it is what keeps a shared Odoo usable as you grow. We design per-role views with filtered choices, computed guards and the Python constraints that back them up.

Design our role-based views

How this page was produced

This page was verified by reading filterable_selection_field.js on the Odoo 19.0 branch, including the if/else-if precedence chain and the current-value passthrough, plus the base selection_field.js it extends. The widget's history was traced by reading the 16.0 loyalty version and diffing the web core file across 17.0, 18.0, 19.0 and the development branch. The loyalty usage example is quoted from the shipped 19.0 views. Spotted an error or a version difference? Tell us and we will correct the page.