The most common ORM functionalities - create(), write(), search(), and unlink() - are learned very quickly by the average Odoo developer. These methods are well-documented, discussed regularly, and generally understood by the community; they form the basis of business logic on a day-to-day level.
Behind its familiar interface, many of Odoo’s advanced features quietly rely on powerful but lesser-known ORM capabilities. One of these is copy(), which the vast majority of developers neither really understand nor use deliberately in their code, even as nearly all have used it indirectly.
Understanding how copy() really works behind the scenes is relevant in Odoo 19, where scalability, modular design, and automation-driven workflows are much more important than ever before. When working correctly, the copy() method is often the cleanest and safest way to duplicate sales orders, clone intricate configuration records, or develop Studio-like customization tools.
What is the copy() method in the Odoo ORM?
The copy() method is used to duplicate an existing record into a new one.
Basic syntax:
new_record = record.copy()
This creates:
- A new database record
- With values copied from the original
- Using the model’s copy() and copy_data() logic
Internally, copy():
- Reads the record data
- Applies copy=False field rules
- Overrides values using default
- Calls create() with the final values
Why is copy() considered a rare ORM method?
Since copy() is rarely called explicitly in code, it is rarely covered in detail, even though it is a component of the core ORM. When users click Action > Duplicate in the user interface, it usually happens automatically. Because of this, a lot of developers use it without realizing how it affects business logic and data consistency.
Copy() is still underutilized in part because developers frequently turn to:
- Manually reading record values
- Removing unwanted fields
- Recreating records using create()
Although this method is effective, it circumvents crucial ORM protections that copy() already offers.
The Role of copy=False in Field Definitions
One of the most important aspects of copy() is its respect for the copy=False attribute on fields. Fields marked with copy=False are automatically excluded from duplication.
Common examples include:
- Sequence numbers
- Reference fields
- Workflow states
- System-managed or audit fields
This ensures that duplicated records do not accidentally reuse values that are meant to be unique or historical.
Using default to Control Duplication Behavior
The default dictionary is where copy() really shines. Developers can alter particular values when they duplicate using this parameter.
Typical use cases include:
- Resetting the states of the workflow
- Dates and references for clearing
- Renaming records that are duplicates
- Using fresh timestamps or ownership
Because of this, copy() becomes a controlled initialization mechanism in addition to a duplication method.
Controlling Record Duplication with copy()
When you need to enforce business rules during duplication, overriding copy() is the recommended approach.
Example: Customizing Sale Order Duplication
from odoo import models, fields
class SaleOrder(models.Model):
_inherit = 'sale.order'
def copy(self, default=None):
default = dict(default or {})
# Reset workflow state
default['state'] = 'draft'
# Update the order name
default['name'] = 'Copy of ' + self.name
# Clear and reset dates
default['commitment_date'] = False
default['date_order'] = fields.Datetime.now()
return super().copy(default)
In this example:
- The duplicated Sales Order always starts in Draft
- The name clearly indicates it is a copy
- Old commitment dates are removed
- The order date reflects the current duplication time
This ensures that duplicated orders behave like new transactions, not historical records.
Common Mistakes When Using copy()
- Workflow states are not being reset.
- Ignoring copy=False fields
- Accidentally duplicating sensitive data
- Misunderstanding relational field behavior
- Reimplementing duplication logic manually
If you use copy() correctly, you can avoid all of these.
Best Practices for Using copy() in Odoo 19
- Always call super().copy(default).
- Keep duplication logic simple and safe.
- Reset fields linked to workflows and history.
- Be careful with relational fields.
- Prefer copy() instead of creating records manually.
The copy() ORM method remains one of the least appreciated but very valuable functionalities of Odoo. When properly utilized, it provides an efficient, safe, and ORM-compliant mechanism for copying records while retaining business integrity. The efficiency of copy() becomes imperative in Odoo 19, where automation, scalability, and clean coding by programmers become essential.
To read more about Complete Overview of Rare ORM Methods in Odoo 19, refer to our blog Complete Overview of Rare ORM Methods in Odoo 19.