Odoo provides several mechanisms to load data into yourk database
during module installation. Whether you're initializing master data,
defining access rights, or executing methods, you can leverage XML
and CSV files to automate these operations.
Loading Data Using XML Files
XML files are the most common way to define and load data in Odoo
modules. You typically place them in a data/ directory inside your
custom module.
Basic XML Record Structure
<record model="model.name" id="record_id">
<field name="field_name">value</field>
</record>
- model: The technical name of the model (e.g.,
res.partner)
- id: A unique identifier for the record within this module
(used as an XML ID)
- field: Each field to set for this record
Example: Creating a Contact Record
To define a contact record using XML, create a file
res_partner_data.xml in the data/ directory of your module (e.g.,
custom_contacts).
<odoo>
<data>
<record id="partner_1" model="res.partner">
<field name="name">Aby Wills < /field>
< field name="phone">9865432344 < /field>
< field name="email">aby@example.com < /field>
</record >
</data >
</odoo>
Then, reference it in your __manifest__.py file:
'data': [
'data/res_partner_data.xml',
],
Once you install the module, the contact will be created
automatically in the system.
Loading Data Using CSV Files
CSV files are ideal for importing large volumes of data or defining
access control lists (ACLs).
Format and Structure
- The file should be named after the model (e.g.,
ir.model.access.csv)
- The first row lists field names, including id (external ID)
- Each following row represents one record
Example: account.tax.group.csv
id,name,country_id/id
tax_group_0,TVA 0%,base.lu
tax_group_3,TVA 3%,base.lu
tax_group_6,TVA 6%,base.lu
When the module is installed, records will be inserted into the
account.tax.group model.
noupdate: Preserve Data on Module Update
The noupdate="1" attribute ensures that Odoo doesn't re-apply the
data when the module is upgraded. This is useful for preserving
user-modified records.
<odoo>
<data noupdate="1">
<record id="res_partner_data" model="res.partner">
<field name="name">Demo Partner</field>
<field name="email">demo@gmail.com</field>
</record>
</data>
</odoo>
Once created, this partner will not be updated in future upgrades,
even if the XML changes.
forcecreate: Always Ensure Creation
The forcecreate="1" attribute forces record creation only if it
doesn't exist yet. This avoids duplication and ensures idempotency.
<record forcecreate="True" id="decimal_payment" model="decimal.precision">
<field name="name">Payment Terms</field>
<field name="digits">6</field>
</record>
Odoo will create this record only if one with the same XML ID doesn't
already exist.
Understanding External IDs
External IDs (also called XML IDs) are persistent identifiers used by
Odoo to manage data records created via XML/CSV.
- Format: module_name.record_id (e.g., sale.mail_act_sale_upsell)
- Used to update existing records during module updates
Example:
<record id="base.main_company" model="res.company">
<field name="name">My Company (San Francisco)</field>
</record>
This updates the name of the company record with ID
base.main_company.
You can view any record’s External ID via Developer Tools > View
Metadata in debug mode.
Namespaces in External IDs
If an external ID contains exactly one dot (.), Odoo treats the part
before the dot as the module name (namespace).
<record id="product.decimal_precision" model="decimal.precision"<
<field name="name">Product UoM</field<
</record<
If no dot is present, Odoo automatically prepends the current module
name.
Deleting Records Using XML
You can remove previously created records using the tag in
two ways:
1. Delete by Domain
<delete model="product.category" id="demo_category_1"/>
This will delete the record with external ID demo_category_1 from the
product.category model
2. Delete by Domain
<delete model="sale.order" search="[('partner_id.name', '=', 'Azure Interior')]"/>
This deletes all sale.order records where the partner name is “Azure
Interior”. Be cautious — this can affect user-entered data.
Calling Python Functions from XML
Odoo allows you to execute methods at module install time using the
tag.
1. Without Parameters
<function id="func_call" model="product.product" name="func_without_params"/>
class Product(models.Model):
_inherit = "product.product"
@api.model
def func_without_params(self):
self.create({'name': 'Test'})
2. With Parameters
<function id="func_call_with_params" model="product.product" name="func_with_params">
<value>Cybrosys Technologies</value>
</function>
class Product(models.Model):
_inherit = "product.product"
@api.model
def func_with_params(self, name):
self.create({'name': name})
You can also pass domains or evaluated expressions using the eval
attribute:
<value eval="[('name', '=', 'My Product'), ('active', '=', True)]"/>