Skip to content

Odoo Default Values Patterns: Complete Guide

DeployMonkey Team · March 24, 2026 10 min read

Default Values in Odoo

Setting correct default values saves users time and reduces errors. Odoo provides multiple mechanisms for defaults, each suited to different scenarios. Understanding which to use prevents confusion and ensures defaults work consistently across form views, API calls, and imports.

Static Defaults

The simplest form — a fixed value assigned when the field is defined:

name = fields.Char(default='New')
active = fields.Boolean(default=True)
state = fields.Selection([
    ('draft', 'Draft'),
    ('confirmed', 'Confirmed'),
], default='draft')
priority = fields.Integer(default=0)
date = fields.Date(default=fields.Date.today)

Note: fields.Date.today without parentheses is a callable reference, evaluated at record creation time. Using fields.Date.today() with parentheses would freeze the date to module load time.

Lambda Defaults

For dynamic defaults that depend on the environment:

# Current user
user_id = fields.Many2one(
    'res.users',
    default=lambda self: self.env.uid,
)

# Current company
company_id = fields.Many2one(
    'res.company',
    default=lambda self: self.env.company.id,
)

# Current date/time
create_date = fields.Datetime(
    default=lambda self: fields.Datetime.now(),
)

# Computed default from config
max_items = fields.Integer(
    default=lambda self: int(
        self.env['ir.config_parameter'].sudo().get_param(
            'my_module.max_items', '10'
        )
    ),
)

Context Defaults

Pass defaults through action context — useful for creating records from specific views:

<!-- In XML action definition -->
<field name="context">{
    'default_partner_id': active_id,
    'default_type': 'incoming',
    'default_state': 'draft',
}</field>

In Python, context defaults are passed during create:

self.with_context(
    default_partner_id=partner.id,
    default_type='incoming',
).env['stock.picking'].create({'name': 'New Picking'})

Context defaults override field-level defaults. The naming convention is default_fieldname.

ir.default Records

User-configurable defaults stored in the database:

# Set a default for all users
self.env['ir.default'].set(
    'sale.order', 'payment_term_id', payment_term.id
)

# Set a default for a specific user
self.env['ir.default'].set(
    'sale.order', 'payment_term_id', payment_term.id,
    user_id=specific_user.id,
)

# Set a default for a specific company
self.env['ir.default'].set(
    'sale.order', 'payment_term_id', payment_term.id,
    company_id=company.id,
)

Users can also set defaults through the UI: right-click a field → Set Default.

default_get Override

For complex default logic that depends on multiple factors:

@api.model
def default_get(self, fields_list):
    defaults = super().default_get(fields_list)
    
    # Set default warehouse based on user's stock location
    if 'warehouse_id' in fields_list:
        warehouse = self.env['stock.warehouse'].search([
            ('company_id', '=', self.env.company.id),
        ], limit=1)
        defaults['warehouse_id'] = warehouse.id
    
    # Copy values from a template record
    if self.env.context.get('template_id'):
        template = self.env['my.template'].browse(
            self.env.context['template_id']
        )
        defaults.update({
            'name': template.name,
            'description': template.description,
        })
    
    return defaults

Company-Dependent Defaults

For fields that should have different defaults per company:

class ResPartner(models.Model):
    _inherit = 'res.partner'

    property_payment_term_id = fields.Many2one(
        'account.payment.term',
        company_dependent=True,
    )

Company-dependent fields store values in ir.property per company. Each company can have a different default value.

Default Priority Order

When multiple default mechanisms apply, Odoo resolves them in this order (highest priority first):

  1. Context defaults (default_fieldname in context)
  2. ir.default records (user-specific, then company-specific, then global)
  3. default_get() override
  4. Field-level defaults (static or lambda)

Common Patterns

Sequence Default

name = fields.Char(default='New', copy=False, readonly=True)

@api.model_create_multi
def create(self, vals_list):
    for vals in vals_list:
        if vals.get('name', 'New') == 'New':
            vals['name'] = self.env['ir.sequence'].next_by_code('my.model')
    return super().create(vals_list)

Copy Default (Prevent Copying)

# Fields that should not be copied when duplicating records
name = fields.Char(copy=False)  # Gets field default instead
state = fields.Selection([...], copy=False, default='draft')
amount_paid = fields.Float(copy=False)  # Reset to 0

Common Pitfalls

  • Parentheses on callablesdefault=fields.Date.today (correct, evaluated per record) vs. default=fields.Date.today() (wrong, fixed at module load).
  • Context defaults silently override — If a view action has default_state in context, your field-level default is ignored.
  • default_get and required fields — default_get runs before validation. Return values for required fields to prevent errors.
  • Lambda and self.env — Lambda defaults have access to self.env but NOT to self (there is no record yet).