Skip to main content
iVentureTeam

reference

One field, two questions: which model, then which record. reference is the widget behind that pair of inputs, and it accepts far more options than it admits to.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 27, 2026Updated August 27, 20267 min read
Technical namereference
Field typesreference, char
Viewsform, list, and inside x2many subviews
Moduleweb, present in every Odoo database
Used in core1 direct usage in core views, in snailmail, but the widget is the default renderer for every reference field
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0
No-code setupNo. Studio has no Reference field type, so both the field and the widget are XML work
Alternativesmany2one, selection, char

What the reference field does

Most relational fields in Odoo know their target model in advance: a salesperson is always a user, a customer is always a partner. Some do not. An activity can be attached to an order or a lead, a mailing can target anything, a log line can point at whatever raised it. Odoo's answer is the reference field, which stores a model name and a record id together in one column, and reference is the widget that edits it.

On screen it is two controls in a row: a plain dropdown listing the models the field allows, and next to it a normal record autocomplete. Choose the model, then choose the record. Once a value is set, the autocomplete behaves exactly like a many2one, with the same open, create and search behavior.

The widget also handles a second, less obvious case. If you put it on a char field whose value looks like res.partner,42, it reads that string, fetches the display name of the target record and renders the same two controls. That is how a text column can be made to behave like a relation without a schema change.

What this means for your team

Reference fields exist for the parts of a business process that legitimately touch several object types. A complaint that might be about an invoice, a delivery or a subscription. An internal task whose source could be a lead or a helpdesk ticket. Forcing that into three separate many2one columns produces forms full of empty fields and reports nobody trusts.

The cost is that a reference is harder to report on than a many2one, because the target model varies row by row. The usual compromise in real projects is to keep the reference for capture and add a computed helper field for the one model you actually report on. Decide that early; retrofitting it after a year of data is the expensive version.

The model_field option is the underrated part. When a configuration record already holds the model in a many2one to ir.model, pointing the widget at it removes the second dropdown entirely and leaves users with one intuitive question instead of two. Automation rules and mailing configuration screens use exactly this shape.

Supported options in Odoo 19

Two options are declared in the widget's descriptor. Everything else in the table below is inherited, because the widget's extractProps calls the many2one extractor and passes the result straight into the record autocomplete. Those inherited options work and are not declared in this widget's supportedOptions, so no developer tooling will suggest them. Read from reference_field.js and many2one_field.js, Odoo 19.0.

OptionTypeWhat it does
hide_modelbooleanHides the model dropdown and keeps only the record autocomplete. Use it when the model is fixed by context or by a default value.(default: false)
model_fieldfieldName of a many2one to ir.model on the same record. The widget reads the technical model name from it and hides the selector. Anything that is not a many2one to ir.model raises an explicit error.
no_openboolean<strong>Inherited and undeclared.</strong> Removes the internal link that opens the referenced record.(default: false)
no_createboolean<strong>Inherited and undeclared.</strong> Removes both creation paths from the record autocomplete.(default: false)
no_quick_createboolean<strong>Inherited and undeclared.</strong> Removes the create-from-typed-text entry, keeping the popup form path.(default: false)
no_create_editboolean<strong>Inherited and undeclared.</strong> Removes the create-and-edit popup, keeping the create-from-text entry.(default: false)
search_thresholdnumber<strong>Inherited and undeclared.</strong> Minimum number of typed characters before the record search runs. Without it, the search fires on focus.
create_name_fieldfield<strong>Inherited and undeclared on the many2one widget too.</strong> Field the typed text is written into when quick-creating.
can_scan_barcodeboolean<strong>Inherited and undeclared on the many2one widget too.</strong> Enables the barcode scan affordance on the autocomplete.(default: false)

hide_model and model_field overlap. The source comment spells out the intent: the model dropdown is shown in exactly one case, when neither option is set. Setting model_field alone already hides the selector, so adding hide_model on top of it changes nothing. And when model_field points at something that is not a many2one to ir.model, the widget throws an explicit error rather than failing quietly.

Working examples

Plain two-step picker

<field name="resource_ref" widget="reference"/>

Model dropdown plus record autocomplete. This is also what a reference field renders without any widget attribute, since the widget is the type's default.

Model driven by another field

<field name="model_id"/>
<field name="resource_ref" widget="reference"
       options="{'model_field': 'model_id'}"/>

One dropdown disappears. Change model_id and the reference is cleared automatically, so no orphan value survives.

Locked model, no creation from the dropdown

<field name="resource_ref" widget="reference"
       options="{'hide_model': True, 'no_create': True, 'no_open': True}"/>

no_create and no_open are inherited from the many2one widget and are not declared here, but they are honored.

A char field behaving as a relation

<field name="target" widget="reference"/>
<!-- target is Char and stores e.g. "res.partner,42" -->

Supported on purpose: supportedTypes lists char alongside reference. The display name is fetched with a separate read.

Seven options it accepts and never declares

The interesting part of this file is the gap between what the widget declares and what it actually accepts. Its supportedOptions array has exactly two entries, hide_model and model_field. Its extractProps, however, starts by calling the shared many2one extractor and only then adds its own two props.

That extractor reads seven more keys: no_create, no_create_edit, no_open, no_quick_create, search_threshold, create_name_field and can_scan_barcode. All seven therefore work on a reference field. The last two are undeclared even on the many2one widget itself, which makes them doubly invisible. It also honors the can_create and can_write attributes on the field element.

The second thing worth knowing is how the value is kept in sync. When model_field is used, a record observer watches that many2one. On every change it reads the technical model name from ir.model and, if a model was already selected before, writes false into the reference field. That is deliberate: keeping a sale.order id after switching the model to crm.lead would produce a value that resolves to the wrong record.

In char mode a similar observer fetches the display name, and the result is stored in the model's specialDataCaches keyed by the raw value. The cache is per client session, so a target record renamed elsewhere keeps showing its old name until the page is reloaded.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Options unchanged; the development branch adds a sync fix for the model selector. See below.
Odoo 19.0VerifiedVerified against the shipped source. Rebuilt on the shared Many2One component; relational values became objects instead of tuples.
Odoo 18.0VerifiedSame two options and same behavior. Rendered through the Many2OneField component.
Odoo 17.0VerifiedSame two options. This is the version that introduced the descriptor and the declared option list.
Odoo 16.0VerifiedBoth options work but neither is declared; the component was registered directly and the prop was called hideModelSelector.

Upgrade note. The two options and the two supported field types have been stable since Odoo 16, so view XML carries over unchanged. What moved is the internals: Odoo 16 registered the component class directly and named its prop hideModelSelector; Odoo 17 introduced the descriptor with the declared options; Odoo 19 rebuilt the render path on the shared Many2One component and switched relational values from tuples to objects. Custom JavaScript that patched this widget on 16, 17 or 18 needs rewriting for 19; XML does not.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. The development branch is unstable, so treat this as a direction of travel and not a promise; we re-verify this page once Odoo 20 ships.

Options are unchanged. hide_model, model_field, the supported types and the inherited many2one behavior are all identical to Odoo 19.

One real fix. The development branch adds a third record observer for the standard case: when the stored value carries a model, the widget now syncs its internal current-relation state with it. On Odoo 19 that state is only set when the user picks a model, which is what makes a freshly loaded record occasionally render its selector as unset. The rest of the diff is the Owl props migration, which affects patches rather than views.

Common problems and fixes

SymptomCause and fix
The model dropdown will not disappearNeither hide_model nor model_field is set. The source shows the selector renders in exactly that one case. Set one of the two options. Setting both is harmless but adds nothing.
Error about model_field not being a many2one to ir.modelThe named field is a char, a selection, or a many2one to another model. Point model_field at a real many2one('ir.model') field, which is what automation-style models use.
The reference empties itself when I change the modelIntended behavior. Changing the model field clears the reference so it cannot point at a record of the wrong model. Pick the model first, then the record. If users need both preserved, split them into two fields.
The referenced record shows an old nameIn char mode the display name is fetched once and cached per raw value for the session. Reload the page. If names change often, store a real reference field instead of a char.
no_create is ignoredUsually a typo in the options dictionary, since the option itself is honored but never declared, so nothing validates it. Check spelling carefully. Undeclared options fail silently by design.
The record autocomplete stays emptyNo model is selected yet; the autocomplete only renders once a relation is known. Choose a model, or supply one through model_field or a default value.
The field will not save on a char columnThe stored string does not match the model,id shape the widget parses. Store values as res.partner,42 with no spaces, or convert the column to a real reference field.

Reference field vs the alternatives

WidgetBest forKey difference
referenceFields whose target model genuinely varies from record to recordStores model and id together and renders a model selector next to the record autocomplete
many2oneA relation with one known target modelDatabase-level foreign key, far easier to filter, group and report on
selectionChoosing the model only, with no record behind itPlain value list, no record lookup and no relation
charStoring a raw model,id string without any pickerNo model dropdown, no autocomplete, no display name resolution

The rule of thumb: if the target model is known when you design the field, use a many2one and let the database enforce it. Use a reference only when the model genuinely varies per record, and expect to pay for it in reporting. If you find yourself supporting exactly two models, two many2one fields plus a small selection is usually easier to live with than one reference.

Frequently asked questions

What is the difference between a reference field and a many2one?+
A many2one always points at one model and is a real foreign key. A reference stores the model name and the id together in a single column, so different records can point at different models. That flexibility costs you database-level integrity and makes reporting harder.
How do I hide the model dropdown?+
Set options="{'hide_model': True}", or set model_field, which hides it as a side effect. The source comment states plainly that the selector appears only when neither option is used.
Can I use the reference widget on a char field?+
Yes. supportedTypes lists char alongside reference. The value must look like res.partner,42, and the widget fetches the display name with a separate read.
Do many2one options like no_create work on a reference field?+
Yes. The widget's extractProps calls the many2one extractor first, so no_create, no_open, no_quick_create, no_create_edit and search_threshold are all honored, even though none of them appears in this widget's declared option list.
Why does my reference clear itself?+
Because the model changed. When model_field is used, the widget writes false into the reference whenever the model many2one changes, so a leftover id cannot resolve against the new model.

Modelling something that points at more than one thing?

Reference fields are usually a sign of a data model decision worth getting right the first time: one flexible column, or a few explicit ones plus reporting that actually works. We do that modelling work as part of Odoo customization on 16 through 19, before it becomes a migration problem.

Book a free consultation

How this page was produced

The declared options, the supported types, the model-change clearing rule and the list of inherited many2one options were read from reference_field.js and many2one_field.js on the Odoo 19.0 branch, and the render behavior from reference_field.xml. The version history comes from reading the same file on the 16.0, 17.0 and 18.0 branches. The Odoo 20 notes come from the public development branch. Spotted an error? Tell us and we will correct the page.