Using XML data files
While CSV files provide a simple and compact format to represent data, XML files are more powerful and give more control over the loading process. For example, their filenames are not required to match the model to be loaded. This is because the XML format is much richer and more information regarding what to load can be provided through the XML elements inside the file.
We already used XML data files in the previous chapters. The user interface components, such as views and menu items, are, in fact, records that are stored in system models. The XML files in the modules are the means used to load these records into the instance database.
To showcase this, we will add a second data file to the
library_app module, data/book_demo.xml, with the following content:<?xml version="1.0"?>
<odoo noupdate="1">
<!-- Data to load -->
<record model="res.partner" id="res_partner_huxley">
<field name="name">Aldous Huxley</field>
</record>
<record model="library.book" id="library_book_bnw">
<field name="name">Brave New World</field>
<field name="author_ids"
eval="[(4, ref('res_partner_huxley'))]" />
<field name="date_published">1932-01-01</field>
</record>
</odoo>
As usual, the new data file must be declared in the
__manifest__.py file:'demo': [
'data/res.partner.csv',
'data/library.book.csv',
'data/book_demo.xml',
],
Similar to the CSV data file we saw in the previous section, this file also loads data into the Library Books Model.
XML data files have an
<odoo> top element, inside of which we can have several <record> elements that correspond to the CSV data rows.Note
The
<odoo> top element in data files was introduced in version 9.0 and replaces the former <openerp> tag. A <data> section inside the top element is still supported, but it's now optional. In fact, now, <odoo> and <data> are equivalent, so we could use either one as top elements for our XML data files.
A
<record> element has two mandatory attributes, model and id, for the external identifier for the record, and contains a <field> tag for each field to write on.
Note that the slash notation in the field names is not available here; we can't use
<field name="publisher_id/id">. Instead, the ref special attribute is used to reference external identifiers. We'll discuss the values of the relational to-many fields in a moment.
You may have noticed the
noupdate="1" attribute in the top <odoo> element. This prevents the data records from being loaded on module upgrades so that any later edits to them are not lost.
When a module is upgraded, the data file loading is repeated, and the module's records are rewritten. It is important to keep in mind that this means that upgrading a module will overwrite any manual changes that might have been made to the module's data.
Note
Notably, if views were manually modified to add customization, these changes will be lost with the next module upgrade. To avoid this, the correct approach is to instead create inherited views with the changes we want to introduce.
This rewrite behavior is the default, but it can be changed so that some of the data is only imported at install time, and is ignored in later module upgrades. This is done using the
noupdate="1" attribute in the <odoo> or <data> elements.
This is useful for data that is to be used as initial configuration but is expected to be customized later, because these manually made customizations will be safe from module upgrades. For example, it is frequently used for record access rules, allowing them to be adapted to implementation-specific needs.
It is possible to have more than one
<data> section in the same XML file. We can take advantage of this to separate data to import only once, with noupdate="1", and data that can be re-imported on each upgrade, with noupdate="0". noupdate="0" is the default, so we can just omit it if we prefer. Note that we need to have a top-level XML element, so in this case, we will use two <data> sections. They must be inside a top level <odoo>or <data> element.Note
The
noupdate attribute can be tricky when developing modules, because changes made to the data later will be ignored. One solution is to, instead of upgrading the module with the -u option, re-install it using the -i option. Reinstalling from the command line using the -i option ignores the noupdate flags on data records.
In an XML data file, each
<record> element has two basic attributes, id and model, and contains <field> elements that assign values to each column. The id attribute corresponds to the record's external identifier and the model attribute corresponds to the target model. The <field> elements have a few different ways to assign values. Let's look at them in detail.
The value to write is the element content: the text between the field's opening and closing tag. For dates and date-times,
eval attributes with expressions returning date or datetime objects will work. Returning strings with "YYYY-mm-dd" and "YYYY-mm-dd HH:MM:SS" will be properly converted. For boolean fields, the "0" and "False" values are converted to False, and any other non-empty values will be converted to True .
A more elaborate alternative for setting a field value is the
eval attribute. It evaluates a Python expression and assigns the result to the field.
The expression is evaluated in a context that, besides Python built-ins, also has some additional identifiers that are available to build the expression to evaluate.
To handle dates, the following Python modules are available:
time, datetime, timedelta, and relativedelta. They allow you to calculate date values, something that is frequently used in demonstration and test data, so that the dates used are close to the module installation date. For more information about these Python modules, see the documentation at https://docs.python.org/3/library/datatypes.html.
For example, to set a value to yesterday, we will use the following code:
<field name="date_published"
eval="(datetime.now() + timedelta(-1))" />
Also available in the evaluation context is the
ref() function, which is used to translate an external identifier into the corresponding database ID. This can be used to set values for relational fields. As an example, we can use it to set the value for publisher_id:<field name="publisher_id" eval="ref('res_partner_packt')" />
For many-to-one relation fields, the value to write is the database ID for the linked record. In XML files, we usually know the XML ID for the record, and we need to have it translated into the actual database ID.
One way is to use the
eval attribute with a ref() function, like we just did in the previous section.
A simpler alternative is to use the
ref attribute, which is available for <field> elements. Using it to set the value for the publisher_id many-to-one field, we would write the following:<field name="publisher_id" ref="res_partner_packt" />
For one-to-many and many-to-many fields, instead of a single ID, a list of related IDs is expected. Furthermore, several operations can be performed—we may want to replace the current list of related records with a new one, or append a few records to it, or even unlink some records.
To support write operations on to-many fields, we use a special syntax in the
eval attribute. To write to a to-many field, we use a list of triples. Each triple is a write command that does different things according to the code used in the first element.
To overwrite the list of authors of a book, we would use the following code:
<field name="author_ids"
eval="[(6, 0,
[ref('res_partner_alexandre'),
ref('res_partner_holger')]
)]"
/>
To append a linked record to the current list of the authors of a book, we would use the following code:
<field name="author_ids"
eval="[(4, ref('res_partner_daniel'))]"
/>
The preceding examples are the most common. In both cases, we used just one command, but we could chain several commands in the outer list. The
append (4) and replace (6) commands are the most used. In the case of the append (4), the last value of the tripled is not used and is not needed, so it can be omitted, as we did in the preceding code sample.
The complete list of available commands is as follows:
(0, _ , {'field': value})creates a new record and links it to this one.(1, id, {'field': value})updates the values on an already linked record.(2, id, _)removes the link to and deletes theidrelated record.(3, id, _)removes the link to, but does not delete, theidrelated record. This is usually what you will use to delete related records on many-to-many fields.(4, id, _)links an already existing record. This can only be used for many-to-many fields.(5, _, _)removes all the links, without deleting the linked records.(6, _, [ids])replaces the list of linked records with the provided list.
The
_ underscore symbol used in the preceding list represents irrelevant values, usually filled with 0 or False.
If we go back to Chapter 3, Your First Odoo Application, we will find elements other than
<record> in the XML files, such as <act_window> and <menuitem>.
These are convenient shortcuts for frequently used models, with a more compact notation compared to the regular
<record> elements. They are used to load data into base models, supporting the user interface, and will be explored in more detail later, in Chapter 10, Backend Views – Designing the User Interface.
For reference, these are the shortcut elements available, along with the corresponding models they load data into:
<act_window>is for the window action model,ir.actions.act_window<menuitem>is for the menu items model,ir.ui.menu<report>is for the report action model,ir.actions.report.xml<template>is for QWeb templates stored in their.ui.viewmodel
Note
Changes in Odoo 11The <url> tag was deprecated and removed. In previous versions, it was used to load records for the URL action model, ir.actions.act_url.
It is important to note that, when used to modify existing records, the shortcut elements overwrite all the fields. This differs from the
<record> basic element, which only writes to the fields provided. So, for cases where we need to modify just a particular field of a user interface element, we should do it using a <record> element instead.
So far, we have seen how to add or update data using XML files. But XML files also allow you to delete data and execute arbitrary model methods. This can be useful for more complex date setups.
To delete a data record, we can use the
<delete> element, providing it with either an ID or a search domain to find the target records.<delete
model="res.partner"
search="[('id','=',ref('library_app.res_partner_daniel'))]"
/>
If we know the specific ID to delete, we can use it with the
id attribute instead. This was the case for the previous example, so it could also be written like this, for the same effect:<delete model="res.partner" id="library_app.res_partner_daniel" />
An XML file can also execute arbitrary methods during its load process through the
<function> element. This can be used to set up demo and test data.
For example, the Notes app, which is bundled with Odoo, uses it to set up demonstration data:
<data noupdate="1">
<function
model="res.users"
name="_init_data_user_note_stages"
eval="[]" />
</data>
This calls the
_init_data_user_note_stages method of the res.users Model, passing no arguments. The argument list is provided by the eval attribute, which is an empty list in this case.
Comments
Post a Comment