Introduction
In Odoo, the ORM (Object-Relational Mapping) is used to interact with the database through Python models. One of its commonly used methods is `create()`, which is called when a new record needs to be added.
Developers often override `create()` to add their own logic, such as generating a sequence number or setting default values. But when Odoo creates multiple records at once, handling them efficiently becomes important. This is where `@api.model_create_multi` comes in.
In this blog, we will understand what `@api.model_create_multi` does in Odoo 19, why it is useful, and how to use it with simple examples.
1. Understanding create() in Odoo
Before explaining `@api.model_create_multi`, let’s first understand the method it is used with: `create()`.
In Odoo, models represent business objects such as customers, products, employees or assets. The ORM provides methods that allow developers to interact with these records without directly writing SQL queries. One of these methods is `create()`, which is used to create new records.
Let’s create a simple custom model called Asset:
from odoo import models, fields
class Asset(models.Model): _name = 'asset.asset' _description = ‘Assets’
name = fields.Char(string="Name") asset_code = fields.Char( string=’Asset Code’, copy=False, readonly=True, default=’New’ ) |
A new asset can be created using
asset = self.env['asset.asset'].create({ 'name': 'Dell Laptop',}) |
Here, the dictionary contains the values that will be used to create the new record.
Why would we override `create()`?
In Odoo 19, `create()` accepts a list of dictionaries containing the values for the records to be created. A single dictionary is also accepted for backward compatibility. This means that an overridden `create()` method needs to be prepared to handle the list-based form when records are created in batch. This is where `@api.model_create_multi` becomes important.
2. A Traditional/Custom create() Override
The standard `create()` method is enough for simple record creation. However, in real-world projects, we often need to perform additional actions when a record is created.
For example, when creating an asset, we may want to automatically generate an asset code if one is not provided. We can do this by overriding the `create()` method.
Note: The following example demonstrates the traditional single-dictionary style of overriding `create()` using `vals`. In Odoo 19, the documented `create()` API uses `vals_list`, a list of dictionaries, while a single dictionary is still supported for backward compatibility. This example is shown for comparison before moving to the batch-oriented approach with `@api.model_create_multi`.
@api.modeldef create(self, vals): if not vals.get('asset_code'): vals['asset_code'] = self.env['ir.sequence'].next_by_code( 'asset.asset' ) return super().create(vals)
Sequence.xml:<record id="seq_asset_number" model="ir.sequence"> <field name="name">Asset Number</field> <field name="code">asset.asset</field> <field name="prefix">AST-</field> <field name='padding'>5</field> <field name="number_increment">1</field></record> |
|
Here, the sequence uses AST- as the prefix and five digits for the sequence number, producing values such as AST-00001, AST-00002, and so on.
We first check whether an `asset_code` was provided. If it wasn’t, we generate one using an Odoo sequence before passing the values to the original `create()` method.
This approach demonstrates the traditional form of overriding `create()`. However, when our custom logic needs to work with multiple sets of values, we need to handle the values as a list.
This raises an important question: How should we handle custom logic when multiple records are created together?
That’s where `@api.model_create_multi` comes in.
3. What happens when multiple records are created?
Odoo can create multiple records in a single operation. The values for these records are provided as a list of dictionaries, where each dictionary contains the values for one record.
vals_list = [ { 'name': 'Dell Laptop', }, { 'name': 'HP Monitor', },]assets = self.env[‘asset.asset’].create(vals_list) |
When custom logic needs to be applied to each record, the dictionaries in `vals_list` can be processed individually before passing the complete list to Odoo.
This is the situation where `@api.model_create_multi` becomes useful.
4. How Does @api.model_create_multi Work?
`@api.model_create_multi` is a decorator used when overriding Odoo’s `create()` method to handle multiple sets of values.
Note: Odoo 19’s documented `create()` API uses a list of dictionaries for record creation. A single dictionary is still supported for backward compatibility. `@api.model_create_multi` is intended for overrides that handle the list-based form.
For our asset example, we can use `@api.model_create_multi` instead of the traditional `@api.model` approach to automatically generate an asset code for each record when one is not provided.
asset_code = fields.Char( string=”Asset Code”, copy=False, readonly=True, default=”New”, )
@api.model_create_multidef create(self, vals_list): for vals in vals_list: if vals.get('asset_code', 'New') == 'New': vals['asset_code'] = ( self.env['ir.sequence'].next_by_code('asset.asset') or 'New' ) return super().create(vals_list) |
Let’s break down what happens in this method.
`@api.model_create_multi`:
This indicates that the `create()` override is designed to receive and process multiple sets of values through `vals_list`.
The `vals_list` parameter therefore contains a list of dictionaries, for example
[ {'name': 'Dell Laptop', 'asset_code': 'New'}, {'name': 'HP Monitor', 'asset_code': 'New'},] |
for `vals in vals_list` → The loop goes through each dictionary in `vals_list`. This allows us to apply our custom logic to each record before it is created.
Generating the asset code:
If `vals.get( ‘asset_code’, ‘New’ ) == ‘New’:` → Here, we check whether the asset code is missing or still has the default value “New”. If that is the case, we generate a new code using the Odoo sequence:
vals['asset_code'] = ( self.env['ir.sequence'].next_by_code('asset.asset') or 'New') |
The `or ‘New’` provides a fallback in case the sequence does not return a value.
After processing all the values, we pass the complete list to Odoo’s original `create()` method:
return super().create(vals_list) |
This ensures that our custom logic is applied first, while the actual record creation is still handled by Odoo’s ORM.
For example, when two or more assets are created with asset_code set to ”New”, the custom `create()` logic assigns sequence values such as AST-00001, AST-00002, and so on.
After creating the assets, the generated asset codes can be seen in the list view:

Fig: Asset list showing the automatically generated asset codes.
5. Why Use @api.model_create_multi?
Now that we understand how `@api.model_create_multi` works, the next question is: Why should we use it?
The main advantage of `@api.model_create_multi` is that it allows custom `create()` logic to handle multiple records while preserving Odoo’s batch-oriented creation process. Instead of creating each record individually, we can modify the values in `vals_list` and pass the complete list to `super().create(vals_list)`.
In simple terms, `@api.model_create_multi` allows us to apply custom logic to multiple records while letting Odoo’s ORM create them as a batch.
6. Common Mistakes to Avoid.
When working with `@api.model_create_multi`, there are a few common mistakes that can lead to errors or defeat the purpose of using the decorator.
1. Treating `vals_list` as a dictionary:
Since `vals_list` is a list of dictionaries, we cannot access it directly like a dictionary.
@api.model_create_multidef create(self, vals_list): asset_code = vals_list.get('asset_code') |
Instead, we need to process each dictionary inside the list:
@api.model_create_multidef create(self, vals_list): for vals in vals_list: asset_code = vals.get('asset_code') |
Here, `vals` represents one dictionary from the `vals_list`.
2. Forgetting to pass the complete list to `super()`:
After processing the values, we should pass the complete `vals_list` to Odoo’s original `create()` method.
Incorrect:
for vals in vals_list: # Custom logicreturn super().create(vals) |
Correct:
for vals in vals_list: # Custom logicreturn super().create(vals_list)
|
This ensures that all the records in the batch are passed to Odoo.
3. Calling `create()` separately inside the loop:
Another mistake is creating each record individually inside the loop.
for vals in vals_list: self.create(vals) return |
Since `self.create()` calls the same overridden method again, it can lead to recursive calls instead of reaching Odoo’s original implementation.
Instead, process the values in the loop and call `super().create(vals_list)` once:
for vals in vals_list: # Custom logic return super().create(vals_list) |
The key is to process each dictionary when necessary, but pass the complete `vals_list` to `super().create()` once.
7. When Should You Use @api.model_create_multi?
`@api.model_create_multi` is useful when you are overriding `create()` and your custom logic needs to work with multiple records.
Common examples include:
Generating sequence numbers
Setting default values
Preparing or modifying field values
Applying custom validation or business logic
A simple rule to remember:
If you are overriding `create()` and your custom logic needs to handle a list of records, `@api.model_create_multi` is the appropriate decorator to consider.
Conclusion
Understanding how Odoo handles record creation is important when adding custom logic to the `create()` method. When multiple records are created together, `@api.model_create_multi` provides a clean way to process each set of values while keeping the creation process batch-friendly.
By using `vals_list`, applying the required logic to each record, and passing the complete list to `super().create(vals_list)`, we can write `create()` overrides that work well with Odoo’s ORM.
In simple terms, `@api.model_create_multi` helps us to handle multiple record creations correctly while keeping our custom `create()` logic clean and maintainable, and aligned with Odoo’s batch-oriented ORM.
If you are looking for an ERP implementation partner with diverse industry experience feel free to contact us. We have proven track record of successful implementations across various sectors including Odoo for Manufacturing, Odoo for Trading, Odoo for FMCG, Odoo for Oil & Gas, Odoo for Diary, Odoo for Pharma, Odoo for Cosmetic Clinic, Odoo for Contracting Companies, Odoo for HVAC, Odoo for Logistics, Odoo for Automobile, Odoo for Laundry, Odoo for Field Service, Odoo for E-Commerce & many more
ZestyBeanz offers Developer / Consultant outsourcing programs, Chat with us in Whatsapp and Hire Odoo Developers, Mobile Application Developers, Consultants.
#OdooKerala #OdooKochi #OdooTrivandrum #OdooERP #ProjectManagement #OdooVansales #HireOdooDeveloper