In this article we will learn about the some useful functions, tips an tricks used in odoo which might be useful in the context of odoo.
1.compute field
Why @api.depends is important
@api.dependstells Odoo which fields your computed field depends on.- Odoo uses it to automatically recompute your field whenever one of the dependent fields changes.
- If you don’t declare dependencies, Odoo may not update your computed field when related values change.
class HrEmployee(models.Model):
_name = 'hr.employee'
first_name = fields.Char()
last_name = fields.Char()
department_id = fields.Many2one('hr.department')
display_name = fields.Char(compute='_compute_display_name')
@api.depends('first_name', 'last_name', 'department_id')
def _compute_display_name(self):
for rec in self:
rec.display_name = f"{rec.first_name} {rec.last_name}"
if rec.department_id:
rec.display_name += f" ({rec.department_id.name})"Here:
display_namedepends onfirst_name,last_name, anddepartment_id.- Odoo will recompute
display_nameif any of these fields are updated.
What happens if you don’t use @api.depends?
display_name = fields.Char(compute='_compute_display_name')
def _compute_display_name(self):
for rec in self:
rec.display_name = f"{rec.first_name} {rec.last_name}"display_namewill compute only once when the record is created (or when explicitly recomputed)- Changing
first_nameorlast_namelater won’t updatedisplay_nameautomatically - Leads to stale or incorrect data in the UI, dropdowns, or reports
Special Cases
1.Depends on a related field:
@api.depends('department_id.name')If you want to recompute when department_id.name changes, you can use dotted notation.
2.Computed once and stored
display_name = fields.Char(compute='_compute_display_name', store=True)Using store=True + @api.depends allows the value to be stored in DB and updated automatically when dependencies change.
Combine with store=True if you want fast read access and filtering.
3.Dynamic computation, not based on fields
- Sometimes, the value depends on external data (like current date or context)
- In that case, you cannot use
@api.depends. You may need to recompute manually or overrideread().
2.default_get in Odoo
default_get in Odoo, which is a very powerful method for setting default values dynamically when creating records
default_getis a built-in ORM method in Odoo.- Purpose: Return a dictionary of default values for fields when a record is being created.
- It is automatically called when you click “Create” in the form view or use
create()without specifying values.
Signature:
default_get(self, fields_list)- Parameters:
fields_list? List of field names that need default values - Returns:
Dictionary{field_name: default_value}
Default Behavior
By default, Odoo will:
- Use
defaultvalues defined in fields
name = fields.Char(default="New")
active = fields.Boolean(default=True)Call default_get to populate fields that are not explicitly given.
Custom default_get
You can override default_get to compute dynamic defaults based on context, user, company, or related models.
Example – Set default user_id to current user
from odoo import models, fields, api
class Task(models.Model):
_name = 'my.task'
name = fields.Char()
user_id = fields.Many2one('res.users', string="Assigned User")
@api.model
def default_get(self, fields_list):
defaults = super(Task, self).default_get(fields_list)
defaults['user_id'] = self.env.user.id # Set current user as default
return defaultsWhen you open the form to create a new task:
- The Assigned User field will automatically show the current user.
3.Using context in default_get
- Odoo often passes context when creating records.
- You can use it to set dynamic defaults:
@api.model
def default_get(self, fields_list):
defaults = super().default_get(fields_list)
if self.env.context.get('default_project_id'):
defaults['project_id'] = self.env.context['default_project_id']
return defaults- This allows passing defaults through actions, wizards, or server actions.
- Example: Opening a Task form from a Project will prefill
project_id.
Scenario: Auto-fill current employee in a leave request
class HrLeave(models.Model):
_inherit = 'hr.leave'
@api.model
def default_get(self, fields_list):
defaults = super().default_get(fields_list)
employee = self.env['hr.employee'].search([('user_id', '=', self.env.user.id)], limit=1)
if employee:
defaults['employee_id'] = employee.id
return defaultsResult: When the user clicks New Leave, employee_id is automatically set to the logged-in employee.
3.self.env.ref
self.env.ref is a very powerful and commonly used method to retrieve a record using its XML ID. It’s widely used when you want to reference existing records like users, groups, views, actions, or other data defined in XML files.
Syntax:
record = self.env.ref('module_name.xml_id')module_name? The name of the module where the record is defined.xml_id? The XML ID of the record.record? The actual Odoo record object corresponding to that XML ID.
Key Points
- Returns a Recordset:
self.env.refreturns the actual record (a recordset), not just an ID. You can then use it like any other Odoo model object. - Raises an Error by Default:
If the XML ID does not exist, it raises aValueError.
You can avoid this by using the optionalraise_if_not_found=False:record = self.env.ref('module_name.xml_id', raise_if_not_found=False) if record: # do something
- Used for Security, Actions, and Defaults:
- Fetch users or groups (
res.groups) - Fetch actions (
ir.actions.act_window) - Fetch views (
ir.ui.view) - Fetch data records defined in XML (e.g., default categories, states)
Examples
1. Access a user group
group = self.env.ref('base.group_system')
if self.env.user.has_group('base.group_system'):
print("Current user is an Odoo administrator")2. Get an action to open a view
action = self.env.ref('sale.action_orders')
return action.read()[0]3. Assign a record as a default
default_partner = self.env.ref('base.res_partner_1')
order = self.env['sale.order'].create({
'partner_id': default_partner.id
})4. Safe retrieval
record = self.env.ref('some_module.non_existing_xml_id', raise_if_not_found=False)
if record:
# do something with the recordWhy Use self.env.ref Instead of search?
- Direct: You don’t have to search by fields (
search([('name','=','...')])) which can fail if the record is renamed. - Reliable: XML IDs are stable and unique across modules.
- Better for module upgrades: Works even if record IDs change in the database because XML IDs stay the same.
other useful of self.env
1.self.env['model_name']
Access any model in Odoo.
partners = self.env['res.partner'].search([('customer_rank', '>', 0)])
for partner in partners:
print(partner.name)self.env['model_name']gives you access to the model class.- You can then
search,browse,create,write,unlink.
Use case: Access records of any model dynamically.
2. self.env.user
Current logged-in user.
current_user = self.env.user
print(current_user.name)
print(current_user.has_group('base.group_system'))- Shortcut to get the current user.
self.env.user.has_group()? check if the user belongs to a group.
Use case: Restrict actions based on the user or log user activity.
3. self.env.company
Current company context.
company = self.env.company
print(company.name)- Useful in multi-company environments.
- Always fetch company-specific settings or defaults using this.
4. self.env.context
The current context dictionary.
if self.env.context.get('default_type') == 'sale':
print("This is a sale order context")- Context contains info like defaults, active record, language, etc.
- Often passed automatically in actions or wizards.
Use case: Set default values dynamically or pass flags between methods.
5.self.env['model'].browse(ids)
Fetch record(s) by ID(s) without search.
record = self.env['res.partner'].browse(3) # fetch partner with ID 3
records = self.env['res.partner'].browse([3,4,5])- Unlike
search, you don’t need a domain. - Returns an empty recordset if ID doesn’t exist.
Use case: Efficient when you know the record ID(s) already.
6.self.env['model'].search(domain)
Search records using domain filters.
partners = self.env['res.partner'].search([('is_company','=',True), ('country_id.code','=','US')])- Returns a recordset matching the domain.
- Common operators:
=,!=,>,<,>=,<=,in,not in,like.
Use case: Fetch records dynamically with conditions.
7. default_get
Provides default values for a new record.
defaults = self.env['res.partner'].default_get(['name','email'])
print(defaults) # {'name': '', 'email': ''}- Returns a dictionary of default values for fields.
- Useful in wizards, automated actions, or when creating records dynamically.
8. sudo()
Bypass access rights temporarily.
partner = self.env['res.partner'].sudo().create({'name': 'Public Partner'})- Ignores access rights and record rules.
- Very useful for automated scripts, imports, or admin operations.
Warning: Use carefully, as it can bypass security.
9. with_context()
Pass or override context temporarily.
order = self.env['sale.order'].with_context(default_partner_id=3).create({})- ts context for a specific operation.
- Very useful to pass defaults, flags, or temporary conditions.
10. exists()
Check if a record still exists in DB.
partner = self.env['res.partner'].browse(10)
if partner.exists():
print("Partner exists")
else:
print("Partner was deleted")Prevents errors when working with stale recordsets.
11. read()
Get field values as a dictionary.
data = self.env['res.partner'].browse(3).read(['name','email'])
print(data)
# [{'id': 3, 'name': 'John Doe', 'email': '[email protected]'}]- Returns list of dictionaries.
- Useful when you want raw values for JSON or API output.
12. mapped()
Extract field values from recordsets.
partners = self.env['res.partner'].search([])
emails = partners.mapped('email')- Returns a list of values from all records.
- Can also follow relations:
partners.mapped('country_id.name') - Returns a list of the email values.
- Note that partners without an email will return
False. - ['[email protected]', '[email protected]', False]
Use case: Quickly extract fields or related fields.
13. filtered()
Filter a recordset in Python.
partners = self.env['res.partner'].search([])
us_partners = partners.filtered(lambda p: p.country_id.code == 'US')- Unlike
search, filtering is done in Python (after fetching records). - Useful for complex conditions not possible in domain.
14. write()
Update records.
partner = self.env['res.partner'].browse(3)
partner.write({'email': '[email protected]'})Can update multiple records at once: partners.write({'active': False}).
15. create()
Create a new record.
partner = self.env['res.partner'].create({'name': 'New Partner'})- Returns the newly created record.
- Often used in wizards or automation scripts.
Refresh page in odoo
Refreshing a page in Odoo depends on where you want to trigger the refresh (from Python, JavaScript, or XML actions).
1. Refresh Page from Python (Server Action Return)
A. Reload the Current Record / View
Use return {'type': 'ir.actions.client', 'tag': 'reload'}
return {
'type': 'ir.actions.client',
'tag': 'reload',
}This reloads the current view.
2. Reload from a Button (Model Method)
If you have a button on form view:
def action_refresh(self):
return {
'type': 'ir.actions.client',
'tag': 'reload',
}Add button in XML
<button name="action_refresh"
type="object"
string="Refresh"/>
3. Refresh via JavaScript (Odoo Web)
Useful if you’re building custom JS widgets.
A. Reload Entire Page
window.location.reload();B. Reload only the view (preferred for OWL/Odoo 16+)
this.trigger('reload');or for OWL:
this.env.bus.trigger('MODEL_CHANGED');4. Refresh with ir.actions.act_window
Sometimes you want to re-open the same view:
return {
'type': 'ir.actions.act_window',
'res_model': 'your.model',
'view_mode': 'form',
'res_id': self.id,
}5. Hard Refresh (Full Browser Refresh)
Not recommended, but possible:
location.reload(true);Example: Refresh page after saving/updating records
@api.model
def create(self, vals):
rec = super().create(vals)
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
You can refresh the particular section in odoo Ui as well like
1.Approach 1
from odoo import models, fields, api
class ProjectTask(models.Model):
_inherit = 'project.task'
def action_refresh_status_bar(self):
for task in self:
# Example: Move to next stage (custom logic)
next_stage = self.env['project.task.type'].search([], limit=1)
if next_stage:
task.stage_id = next_stage.id
# Return a JS action to trigger status bar refresh
return {
'type': 'ir.actions.client',
'tag': 'reload_model',
'params': {
'model': 'project.task',
'res_id': self.id,
'fields': ['stage_id'], # Only refresh stage_id
}
}
How It Works
- Click Refresh Status.
- Python method updates the
stage_id. - The
reload_modelclient action triggers onlystage_idto refresh in the UI (status bar updates immediately). - Other fields and the rest of the form remain untouched.
reload_modelworks in Odoo 16+.- You can refresh multiple fields by adding them to
fields:['stage_id','user_id']. - No full page reload; partial UI update is smooth.
2.Approach -2
Here’s a pure Python / XML approach in Odoo that allows the status bar to update dynamically without any custom JavaScript. This works with computed or related fields and uses Odoo’s built-in automatic field updates.
Python: Computed Field for Status Display
In your model (models.py):
from odoo import models, fields, api
class ProjectTask(models.Model):
_inherit = 'project.task'
# Computed field to reflect status bar dynamically
dynamic_stage_name = fields.Char(
string="Dynamic Status",
compute='_compute_dynamic_stage',
store=True
)
@api.depends('stage_id')
def _compute_dynamic_stage(self):
for task in self:
task.dynamic_stage_name = task.stage_id.namedynamic_stage_name is a stored computed field, updated automatically whenever stage_id changes.
XML: Show Field in Status Bar or Form View
You can display it in the form header or near the status bar
<record id="view_task_form_inherit_dynamic_status" model="ir.ui.view">
<field name="name">project.task.form.dynamic.status</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_form2"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<!-- Show computed field dynamically -->
<field name="dynamic_stage_name" readonly="1"/>
</xpath>
</field>
</record>How It Works
- Whenever
stage_idchanges (manually or via a button), Odoo automatically recomputesdynamic_stage_name. - The UI updates only that field, showing the current status in real-time.
- No JS or reload needed.
- Can also use
@api.dependsfor multiple fields if your status depends on more thanstage_id
