In this articles we will learn about the odoo ORM and usecase as well. how Odoo ORM can be used in optimal way and other related stuff as well.
Key Odoo ORM Methods & Hook
1.default_get(self, fields_list)
- Purpose: Provide dynamic default values when creating records.
- When it’s called: Automatically when creating a new record (via UI or
create()) - Real-World Use Cases:
- Prefill current user:
user_id = self.env.user - Prefill related record from context:
project_id = context['default_project_id'] - Set default values based on other models: auto-assign employee based on logged-in user
- Prefill current user:
- Always call
super()
2.create(self, vals)
- Purpose: Intercept record creation, modify values, or add custom logic.
- When it’s called: When creating records with
create()or through UI - Real-World Use Cases:
- Auto-generate sequence number for a record
- Validate some fields before creation
- Automatically link related records
Example:
@api.model
def create(self, vals):
if 'name' not in vals:
vals['name'] = self.env['ir.sequence'].next_by_code('my.task') or 'New'
record = super().create(vals)
record.log_creation() # Custom method
return recordKey Note:
Always return super().create(vals) object.
3.write(self, vals)
- Purpose: Intercept updates to records, enforce validations, trigger side-effects.
- When it’s called: On record updates (
record.write({...})) - Real-World Use Cases:
- Prevent a field from being changed under certain conditions
- Auto-update related models when a field changes
- Add audit logs for critical updates
def write(self, vals):
if 'state' in vals and vals['state'] == 'done':
vals['done_date'] = fields.Date.today()
return super().write(vals)4.unlink(self)
- Purpose: Intercept deletion of records
- When it’s called: On
record.unlink() - Real-World Use Cases:
- Prevent deletion of records under certain states (like confirmed invoices)
- Clean up related records
- Raise
UserErrorto prevent deletion
def unlink(self):
for rec in self:
if rec.state == 'done':
raise UserError("Cannot delete completed tasks")
return super().unlink()5.copy(self, default=None)
- Purpose: Override how records are duplicated
- When it’s called: On “Duplicate” button
- Real-World Use Cases:
- Remove or reset unique fields when duplicating (like internal reference)
- Auto-increment a number for the new record
def copy(self, default=None):
default = dict(default or {})
default['code'] = self.env['ir.sequence'].next_by_code('my.code') or '/'
return super().copy(default)6.name_get(self)
- Purpose: Control how records are displayed in many2one or dropdowns
- When it’s called: Whenever the system needs a display name
- Real-World Use Cases:
- Show concatenated fields:
First Last - Add extra info:
[ID] Name - Include status in display name:
Task (Done)
- Show concatenated fields:
def name_get(self):
result = []
for rec in self:
name = f"{rec.code} - {rec.name}" if rec.code else rec.name
result.append((rec.id, name))
return result7.name_search(self, name='', args=None, operator='ilike', limit=100)
- Purpose: Customize search behavior for many2one fields
- When it’s called: When typing in a dropdown/many2one search
- Real-World Use Cases:
- Allow searching by code or name
- Filter out inactive records in search
def name_search(self, name='', args=None, operator='ilike', limit=100):
args = args or []
domain = ['|', ('name', operator, name), ('code', operator, name)] + args
records = self.search(domain, limit=limit)
return records.name_get()8.fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False)
- Purpose: Customize the view dynamically before rendering
- When it’s called: When loading form/tree/kanban view
- Real-World Use Cases:
- Hide fields based on user group
- Change labels or help text dynamically
- Add default filters dynamically
9.onchange Decorator (@api.onchange)
- Purpose: Compute values before saving, triggered on field change in UI
- When it’s called: Only in form view, client-side
- Real-World Use Cases:
- Auto-fill fields when another field is selected
- Update totals dynamically
- Show warning messages in UI
@api.onchange('product_id')
def _onchange_product(self):
if self.product_id:
self.price_unit = self.product_id.list_price10.@api.constrains
- Purpose: Validate fields after creation/update
- When it’s called: On
create()orwrite()automatically - Real-World Use Cases:
- Prevent end date < start date
- Salary must be > 0
- Unique constraint across multiple fields
@api.constrains('start_date', 'end_date')
def _check_dates(self):
for rec in self:
if rec.end_date < rec.start_date:
raise ValidationError("End date cannot be before start date")
