Odoo External ID / XML ID Helper
Stop guessing XML ID naming conventions — and stop retyping env.ref() by hand.
Suggest a name
sale_order_view_form
Add your module name above to see usage snippets.
The basics
What is an external ID (XML ID)?
An external ID
(also called an XML ID) is the stable string Odoo uses to reference
a specific record — stored in
ir.model.data, looked up via
env.ref(), and written as
ref="..." in XML data files. It always
has the shape module.name, e.g.
sale.action_orders — the module prefix is
what keeps the same short name from colliding across modules.
Getting started
How to use this tool
Pick a record type and model, and get a name that follows the widely-used community convention — add your module name to see the full id and every way to reference it.
Paste an existing external ID from your code to validate its format and instantly get its Python/XML usage snippets.
Reference — not a rule
Naming conventions by record type
Odoo doesn't enforce any of this — ir.model.data
accepts any valid identifier as a name. These are the widely-followed
OCA/community conventions this tool's Build mode suggests.
<model>_view_<type> <model>_action <model>_menu group_<name> <model>_rule_<detail> seq_<name> ir_cron_<name> <model>_report_<name> Examples
Referencing an external ID
Every common place an id shows up in Python and XML.
self.env.ref('sale.action_orders') Python — the most common form, inside a model method.
env.ref('sale.action_orders').id Python — the raw database id, when an API needs an integer rather than a recordset.
<field name="res_model_id" ref="sale.action_orders"/> XML — a many2one field pointing at another record.
<field name="group_ids" eval="[(4, ref('base.group_user'))]"/> XML — ref() called inside eval, here to add a group to a many2many via Odoo's (4, id) command.
%(base.group_user)d Old-style % substitution — still parsed by Odoo but considered legacy; prefer eval="ref('...')" in new code.
FAQ
Frequently asked questions
What is an external ID (XML ID) in Odoo?
A stable string identifier for a specific database record — module.name — stored in the ir.model.data table. It's how XML data files, module upgrades, and code reference records that don't have a predictable database id.
Why does an external ID need a module prefix?
Because the same short name (e.g. 'action_orders') is reused across many modules — the module prefix ('sale.action_orders' vs 'purchase.action_orders') is what keeps them from colliding.
Can the name part contain dots?
Yes — Odoo splits an external ID on only its first dot, so 'sale.my.nested.id' is module 'sale' and name 'my.nested.id'. In practice this is rare; most XML IDs use underscores, not dots, within the name.
What's the difference between ref="..." and eval="ref('...')" in XML?
ref="..." is a shorthand only valid for a field that is itself a relation (many2one, many2many); eval="ref('...')" runs Python inside the field value, so it works anywhere a Python expression is expected — e.g. inside an (4, ref('...')) command for a many2many.
Is the %(module.name)d substitution still used?
It still works, but it is a legacy form from older Odoo versions. Modern code prefers eval="ref('module.name')", which is clearer and works in more contexts.
Do I have to follow the naming conventions this tool suggests?
No — Odoo does not enforce them. They are widely-followed OCA/community conventions that make a module easier for other developers (and future you) to navigate, not a technical requirement.
What happens if two modules define the same external ID?
It's not allowed within the same module, but a different module CAN reference and even override another module's record via its full external id — that's a normal, supported pattern (e.g. overriding a view via inherit_id).
How do I find the XML ID of an existing record?
With developer mode enabled, open the record's form view, then Settings icon (or the debug menu) > 'View Metadata' — it shows the external id directly, or tells you the record has none yet.