odoo

Useful Functions in Odoo: Boost Your Development Efficiency

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.depends tells 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_name depends on first_name, last_name, and department_id.
  • Odoo will recompute display_name if 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_name will compute only once when the record is created (or when explicitly recomputed)
  • Changing first_name or last_name later won’t update display_name automatically
  • 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 override read().

 

2.default_get in Odoo

default_get in Odoo, which is a very powerful method for setting default values dynamically when creating records

  • default_get is 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 default values 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 defaults

When 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 defaults

Result: 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

  1. Returns a Recordset:
    self.env.ref returns the actual record (a recordset), not just an ID. You can then use it like any other Odoo model object.
  2. Raises an Error by Default:
    If the XML ID does not exist, it raises a ValueError.
    You can avoid this by using the optional raise_if_not_found=False:
    1. record = self.env.ref('module_name.xml_id', raise_if_not_found=False)
      if record:
          # do something
  3. Used for Security, Actions, and Defaults:
  4. Fetch users or groups (res.groups)
  5. Fetch actions (ir.actions.act_window)
  6. Fetch views (ir.ui.view)
  7. 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 record

Why 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

  1. Click Refresh Status.
  2. Python method updates the stage_id.
  3. The reload_model client action triggers only stage_id to refresh in the UI (status bar updates immediately).
  4. Other fields and the rest of the form remain untouched.
  5. reload_model works in Odoo 16+.
  6. You can refresh multiple fields by adding them to fields: ['stage_id','user_id'].
  7. 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.name

dynamic_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

  1. Whenever stage_id changes (manually or via a button), Odoo automatically recomputes dynamic_stage_name.
  2. The UI updates only that field, showing the current status in real-time.
  3. No JS or reload needed.
  4. Can also use @api.depends for multiple fields if your status depends on more than stage_id

About author

author image

Amrit panta

Fullstack developer, content creator



Scroll to Top