本文目前仅提供英文版本。翻译即将推出。
A user clicks a filter in the search view, the filter chip appears, but the list does not change. Or the URL shows the filter applied but the records returned are unfiltered. Or a search_default_X in the action context is being ignored. There is no error message — the UI just lies about what filter is active. This is one of the most user-frustrating bugs in Odoo 17.0/18.0/19.0.
Quick Fix
Open the filter's definition. The domain attribute must be a valid domain expression with the model's actual field names:
<filter string="Won Opportunities"
name="filter_won"
domain="[('stage_id.is_won', '=', True)]"/>
Test the domain in a shell:
env['crm.lead'].search([('stage_id.is_won', '=', True)]).ids
# returns the records the filter should match
If the shell returns the right records but the UI does not, the problem is in the search view definition or context wiring.
Why This Happens
Search view filters are declared in XML and applied to list/kanban/pivot views via the controller. The domain attribute is parsed at render and intersected with the user's other filters. The five common failure modes:
- Typo in the domain —
'stage_id.is_won'versus'stage_is_won'. Odoo silently treats unknown fields as match-nothing in some versions and match-all in others. - Filter name collision. Two filters with the same
name=. Odoo uses the last one, the first appears to do nothing. search_default_Xin the action context applies on first open but is overridden by user state. The user's saved view state takes precedence.- Inheritance ordering. A custom module inherits the search view and adds a filter, but its inherit position is wrong, and the new filter ends up outside the
<group>that triggers application. - Custom filter widget that does not actually call
searchModel.update()— common in custom JS widgets where the developer forgot to invoke the search update.
Step-by-Step Diagnosis
1. Verify the domain. Test in a shell with the user's session:
domain = [('stage_id.is_won', '=', True)]
env['crm.lead'].with_user(user_id).search(domain).ids
Empty list means the domain is correct but no records match (filter is "working", it just shows nothing). A list with values that are wrong means your filter is not being applied.
2. Inspect the rendered search view.
SELECT arch_db FROM ir_ui_view
WHERE type = 'search' AND model = 'crm.lead' AND active = true;
Read the composed arch. Find the filter with the broken behaviour. Verify the domain attribute string.
3. Check for duplicate name=.
grep -r 'name="filter_won"' /opt/odoo/addons/
Two definitions = collision. Rename one.
4. Reset the user's saved view state.
DELETE FROM ir_filters WHERE user_id = <uid> AND model_id = 'crm.lead';
Then ask the user to retest. If the filter now works, their saved state was overriding the default.
5. Check JS console for search-related errors. F12, Console, reproduce. Custom search widgets may throw silently and cancel the filter application.
Permanent Fix
For typos and bad domains, fix the domain expression. Use chained field access for related lookups:
<filter string="Won This Quarter"
name="filter_won_this_quarter"
domain="[
('stage_id.is_won', '=', True),
('date_won', '>=', context_today().strftime('%Y-%m-01'))
]"/>
For duplicate names, namespace your custom filters:
<filter string="Priority Won"
name="my_module_filter_priority_won" <!-- prefix with module name -->
domain="[('stage_id.is_won', '=', True), ('priority', '=', '3')]"/>
For search_default_X to actually apply, ensure the action's context names a filter that exists:
<record id="action_crm_won" model="ir.actions.act_window">
<field name="name">Won Opportunities</field>
<field name="res_model">crm.lead</field>
<field name="view_mode">list,kanban</field>
<field name="context">{'search_default_filter_won': 1}</field>
</record>
The context key is search_default_<filter_name>. If the filter does not exist, the context is silently ignored.
For inheritance order issues, place your filter inside the right <group> element:
<record id="view_crm_lead_search_inherit" model="ir.ui.view">
<field name="name">crm.lead.search.inherit</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.view_crm_case_leads_filter"/>
<field name="arch" type="xml">
<filter name="my_team" position="after">
<filter string="Won This Quarter"
name="filter_won_this_quarter"
domain="[('stage_id.is_won', '=', True)]"/>
</filter>
</field>
</record>
Anchor by name="...", not by xpath, when possible — it is forgiving to view changes between versions.
For custom widget issues, ensure your widget calls searchModel.search() after updating:
// In your custom widget
this.props.searchModel.toggleSearchItem(filterId);
// or
this.env.searchModel.search(domain);
Without one of these calls, your filter chip appears but the list does not refresh.
How to Prevent It
- Lint domains in CI. Walk every search view, parse every
domain="..."attribute, validate against the model's field set. Catches typos before merge. - Namespace custom filter names. A team rule like "filter names must start with
<module>_" prevents collisions reliably. - Test default filters by URL. A Playwright test that opens
/web#action=<id>and asserts the records list size matches the SQL count is cheap and catchessearch_defaultregressions. - Reset user state between tests. Saved views and user filters can mask bugs. Tests should run with a clean
ir_filtersfor the test user. - Inherit by name, not xpath, for filters.
<filter name="X" position="after">survives view rearrangements that break xpath selectors. - Centralize domain helpers. If you reuse
[('stage_id.is_won', '=', True)]in five places, factor it into a Python helper. Less duplication, less typo risk.
Related Errors
- Form view renders blank — sibling view bug.
- Kanban view not rendering cards — same family.
- List view aggregation shows wrong totals — what looks like wrong filters but is actually wrong totals.
- Tree view shows readonly but allows edits — UI state lying about behaviour, similar pattern.
Frequently Asked Questions
Why does the URL show the filter applied but the list does not change?
Two common causes. First, the user has a saved view that overrides the URL state — clear ir_filters to test. Second, the filter's domain is invalid (typo, missing field), so Odoo silently passes empty or no constraint. Test the domain in a shell.
Can I have a filter that depends on the current user?
Yes, use uid in the domain:
<filter string="My Records" name="my_records"
domain="[('user_id', '=', uid)]"/>
Other useful tokens: context_today(), current_company, active_test=False for archived.
How do I make a search filter the default for a specific group?
Use groups= on the filter and search_default_X on a group-specific action. Or add the filter conditionally in your XML:
<filter string="My Pipeline" name="my_pipeline"
domain="[('user_id', '=', uid)]"
groups="sales_team.group_sale_salesman"/>
My search default works on first open but breaks after the user clicks something. Why?
Because search_default_X only applies on action launch. Once the user interacts with the search bar, their state takes over and is saved as their last view. To "stick" a default, mark the filter default="True" (Odoo 18.0+) which makes it auto-apply on every visit, not just first.
Can a filter call a Python method dynamically?
No, search filters are static domains. For dynamic filters (today's date, current user), use the special tokens (uid, context_today(), current_company). For genuinely complex logic, a custom server action or a stored compute that the filter then queries on is cleaner than runtime Python in the filter.
How do I share a filter across multiple users?
Save it as a "Shared" filter via the search bar's Save Current Search → Make available to all users. This creates an ir.filters row with user_id=False. Other users see it in their Filters dropdown automatically.
Need help with a tricky Odoo error? ECOSIRE's Odoo experts have shipped 215+ modules — get expert help.
作者
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
相关文章
BMF Programmablaufplan Lohnsteuer 2026:实施德国官方工资税计算(XML、API、Odoo)
BMF Programmablaufplan Lohnsteuer 2026 开发人员指南:PAP 是什么、XML 伪代码格式、官方测试服务以及到 Odoo 工资单的映射。
2026 年 CRM 系统的成本是多少? 40 多个实施的实际定价
来自 40 多个实施的真实 CRM 定价:每个用户的许可成本、实施费用、隐藏成本以及 Odoo、HubSpot、Salesforce 等的 3 年 TCO。
eMAG Odoo 集成:将罗马尼亚最大的市场连接到您的 ERP(订单、库存、e-Factura)
将 eMAG Marketplace 连接到 Odoo ERP:报价和订单同步、AWB 运输、退货、库存和价格更新,以及卖家的罗马尼亚 e-Factura 合规性。