Odoo Development
Odoo Data Files, XML IDs and noupdate
Everything in Odoo that is not code is a record, and records shipped by a module are identified by XML IDs. Understanding what that identifier is, and what noupdate does to it on the next upgrade, is the difference between a module that updates cleanly and one that overwrites a customer's configuration every time.
Part of our guide to Odoo Development
Records, not configuration files
Views, menus, actions, email templates, access rights, sequences and scheduled actions are all database records. A module's data files are instructions for creating them.
That has a consequence worth stating plainly: they can be edited after installation. A user can change a view through the interface, and the next module upgrade may or may not overwrite that change. Which of those happens is decided by noupdate.
What an XML ID actually is
An XML ID is a stable name for a record, stored in ir.model.data as a module name plus an identifier. sale.model_sale_order means the identifier model_sale_order belonging to the module sale.
It does two jobs.
It lets modules reference each other's records. Database ids differ between databases, so ref="sale.model_sale_order" is the only portable way to point at something.
It makes upgrades idempotent. When Odoo loads a data file and finds the XML ID already registered, it updates that record rather than creating a second one. Without the identifier, every upgrade would duplicate every record.
Within your own module you can omit the prefix and Odoo assumes yours. Write the prefix when referring to another module's record.
The shape of a data file
Qualified 15 Quotation Sent Your quotation {{ object.name }}
The attributes that do real work:
refpoints at another record by XML ID and resolves to its database idevalevaluates a Python expression, needed for booleans, numbers and listssearchfinds a record by domain instead of naming it, useful when the target has no stable identifier
For Many2many and One2many fields, values use Odoo's command syntax, where (6, 0, [ids]) replaces the whole set and (4, id) adds one.
noupdate is the decision that matters
By default, every module upgrade rewrites the records in its data files back to what the files say. Any local change is lost.
noupdate="1" changes that: the record is created if missing, and left alone forever after.
Welcome Email Welcome to our service
The trade is symmetrical, and neither side is free.
Without noupdate, your module owns the record. Improvements reach every database on upgrade, and every customer edit is discarded. Correct for views, menus and actions.
With noupdate, the customer owns it after creation. Their edits survive, and your later improvements never arrive. Correct for email templates, default configuration, sample records and anything a business user is expected to adjust.
Getting this wrong is a support ticket either way. A customer whose carefully worded email template reverted after an upgrade, or a customer who never received a fix because the record was frozen at version one.
Changing your mind later
Because noupdate is stored per record in ir.model.data, moving a record from one mode to the other on existing databases is not just an edit to the XML file. Existing installations keep the flag they were created with.
Flipping it for records already out in the field means a migration script that updates the noupdate column on the relevant ir.model.data rows. It is a few lines, and it is the kind of thing that gets forgotten until a customer reports the old behaviour.
Data versus demo
Files in the manifest's data list load on every install. Files in demo load only when the database was created with demo data.
Sample records in data end up in production, which is the usual origin of test partners in a live system. A relevant detail for anyone testing on Odoo 19: demo data is off by default there, whereas 18 and 17 load it unless told otherwise, so a demo-dependent test can pass on one version and find nothing on another.
CSV when the shape is uniform
CSV is the better format for many rows of the same shape:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_equipment_check_user,equipment.check.user,model_equipment_check,base.group_user,1,1,1,0
The :id suffix marks a column holding an XML ID rather than a raw value. The filename determines the model, which is why access rights live in a file named ir.model.access.csv.
CSV cannot express evaluated expressions, nested values or conditional loading. When you need those, use XML.
Load order is behaviour
Data files load in the exact order of the manifest data list, and that order is functional rather than cosmetic. A menu referencing an action defined later does not resolve. A record rule referencing a group created later fails the same way.
Test on an empty database before shipping. An upgrade of an existing database finds the referenced records already present from last time and hides an ordering bug that a clean install will surface immediately, usually at a customer.
FAQ
Questions, answered.
What is an XML ID in Odoo?
A named, stable reference to a database record, stored in ir.model.data as a module name and an identifier. It is what lets one module reference another's records by name rather than by database id, and what lets an upgrade recognise a record it shipped previously instead of creating a duplicate.
What does noupdate do in an Odoo data file?
It tells Odoo to create the record if it does not exist and then never overwrite it on subsequent upgrades. Use it for records customers are expected to edit, such as email templates. Leave it off for records your module owns, such as views.
When should I use CSV instead of XML for Odoo data?
CSV is better for many rows of the same shape with simple values, which is why access rights use it. XML is better when you need references to other records, nested values, evaluated expressions or anything conditional.
Keep reading
More on Odoo Development.
The full guide, plus the other articles in this cluster.
With CODEerts
Need something built properly?
We are certified Odoo partners. We build custom Odoo modules that extend the framework rather than fight it, and we publish our own apps on the Odoo Store.
See how we can helpBook a callReady to make Odoo work the way your business does?
Book a free callCODEerts is a team of certified Odoo partners and full-stack engineers. We implement, customise and support Odoo ERP, then build the software around it.