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 defaultsCompany-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):
- Context defaults (
default_fieldnamein context) - ir.default records (user-specific, then company-specific, then global)
- default_get() override
- 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 0Common Pitfalls
- Parentheses on callables —
default=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_statein 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).