Knowledge Base

Simply invoke ./odoo.py scaffold <module_name> <directory> on the command line. This will automatically create a basic module called <module_name> inside <directory>. The module name must be unique within the directory.

The cr attribute on environments is the cursor for the current database transaction and allows executing SQL directly (e.g. for queries which are difficult to express using the ORM or for performance reasons).

You can use self.env.cr.execute('SELECT * FROM table WHERE attribute=%s', [(value)]) and fetch the results in a for cycle by doing for results in self.env.cr.dictfetchall():.

Fields can have values calculated by a method, instead of simply reading a database stored value.

A computed field is declared just like a regular field, but has an additional argument computed with the name of the method used to calculate it like is_admin = fields.Boolean(compute="_compute_is_admin", store=False). If you want to store the computed field in the database, you can use store=True. You can also compute multiple fields at the same time by using the same method on all the computed fields and set the fields like self.field_1 = value_1, so on and so forth.

For showing user_id column in some tree, in the tree xml definition use <field name="user_id" invisible="context.get('invisible_user', True)"/>. In the search use <filter string="Show user" context="{'invisible_user': False}"/>.

There are three different mechanisms to extend models:

  • Using _inherit but leaving out _name, the new model replaces the existing one, this is useful to add/customize fields/methods;
  • Using the _inherit and _name attributes together, Odoo creates a new model using the existing one (provided via _inherit) as a base, the new model gets all the fields and methods from its base;
  • Using the _inherits (note the additional "s") a model delegates the lookup of any field not found on the current model to "children" models.

The delegation is performed via reference fields automatically set up on the parent model and methods are not inherited, only fields.

You can use the eval attribute to evaluate a Python expression and assign the result value to the field like <field name="field_name" eval="python_expression_to_evaluate"/>.

You need to set a new view and use the inherit_id field to identify the view you are extending like <field name="inherit_id" ref="target_view_id"/> before <field name="arch" type="xml"/>

You can then use XPath expressions to locate and insert/change/delete elements. Odoo also provides shortcut notations if you want to avoid XPath syntax like <field name="target_field" position="before"><field name="new_field"/></field>

The position attribute used with the locator element is optional, and can have the following values: after; before; inside; replace; attributes.

By declaring a method with the same name, the method will replace the previous one. In this case, it's best to avoid changing the method's parameters so the existing calls on it will keep working properly. In case you need to add additional parameters, you can make them optional with the default value keyword.

You can change an inherit field by adding a field with the same name on the extended model and set only the values for the attributes to be added/changed. The other attributes will remain unchanged.

An XML file can execute methods during its load process through the <function> element like <function model="model_name" name="method_name" eval="method_parameters"/>. We can use a tuple in the eval attribute to pass parameters to the method.

You can add a computed field like image = fields.Binary('Image', compute='_resize_image', store=True, attachment=True) and use the following methods (which are defined on tools) to automatically resize an image:

  • image_resize_image_big (1024x1024);
  • image_resize_image_medium (128x128);
  • image_resize_image_small (64x64);

You can define _resize_image method like self.image = tools.image_resize_image_medium(self.image) or you can also change the default width and height of the resized image by providing the default size value parameter on those methods like self.image = tools.image_resize_image_medium(self.image, size=(256, 256)).

You can use the attrs attribute in a form field view to dynamically set its attributes based on another field’s value like <field name="field_name" attrs="{'field_attribute': field_domain}"/>.

The attrs takes a Python dictionary and maps element's attributes to domains. For example, you could hide a field like <field name="id" attrs="{'invisible': [('is_admin', '=', False)]}"/>

All records in the database have a unique identifier, the id field, that is a sequential number automatically generated by the database engine.

Since we don't know these IDs beforehand (and we can need them to reference a related record), we use external named identifiers to translate these identifier names into the actual database IDs assigned to them.

The ir.model.data keeps the mapping between the named external IDs and their corresponding numeric database IDs.

To delete a data record we use the <delete> element, providing it with either an id or a search domain to find the target record like <delete model="target_module" id="target_id"/> or <delete model="target_module" search="[('id','=',target_id)]"/>.

You can use the ref attribute to translate an external ID into the corresponding database ID for a many-to-one relation field like <field name="field_name" ref="module_name.external_record_id"/>.

For a one-to-many or many-to-many relation field, a list of IDs is expected, so a different syntax is needed like <field name="field_name" eval="[(4, [ids])]"/>.

In the last case we use a list of triples, each triple is a write command that does different things according to the code used:

  • (0,0,{values}) link to a new record that needs to be created with the given values dictionary;
  • (1,ID,{values}) update the linked record with id=ID;
  • (2,ID) remove and delete the linked record with id=ID;
  • (3,ID) delete the relationship on the linked record with id=ID;
  • (4,ID) adds a relationship to existing record with id=ID;
  • (5) unlink all;
  • (6,0,[IDs]) replace the list of linked IDs.

It means that the records are created the first time they are loaded and in subsequent module upgrades nothing will be done to them. this is used so that certain records (e.g. a email template) can be changed by the user and are not altered after updating the server module.

You shouldn't modify an existing module directly because doing so does not allow a clear separation between the original module and our modifications, making it difficult to apply changes or updates. You should use the inheritance mechanisms to extend modules.

The environment stores various contextual data used by the ORM:

  • The database cursor for database queries;
  • The current user for access rights checking
  • The current context storing arbitrary metadata.

The environment also stores caches. All recordsets have an environment, which is immutable, can be accessed using env and gives access to the current user (cr), the cursor (cr) or the context (context).

XPath expressions are expressions to select nodes in an XML document. We can use XPath expressions to locate, insert or delete elements in XML. You can find more in:

https://www.w3.org/TR/xpath/

Place your .png icon in your_module_directory/static/description/icon.png. The default size is 200x200 pixels.

Declare a button like <button name="method_name" type="object" string="text_to_display_on_button"/> in the form then define the method with the same method name like def method_name(self):. Remember that methods must always return something, if they return None, client calls using the XMLRPC protocol won’t work. If you have nothing to return, it's common practice to return True.