What is Context in Odoo?
The context is a dictionary attached to every Odoo environment that carries metadata about the current operation — language, timezone, active company, default values, and behavior flags. with_context() creates a new environment with modified context values, allowing you to control how ORM operations behave.
Basic Usage
Adding Context Values
# Add or override context keys
records = self.with_context(my_flag=True).search([])
# Multiple values
records = self.with_context(
default_partner_id=partner.id,
tracking_disable=True,
).create(vals)Reading Context
# In any method
partner_id = self.env.context.get('default_partner_id')
is_import = self.env.context.get('import_compat', False)
lang = self.env.context.get('lang', 'en_US')Common Context Keys
| Key | Purpose | Example |
|---|---|---|
| lang | Translation language | 'fr_FR', 'de_DE' |
| tz | Timezone | 'Europe/Paris' |
| allowed_company_ids | Multi-company access | [1, 2, 3] |
| active_test | Include archived records | False to include |
| tracking_disable | Disable chatter tracking | True during imports |
| mail_create_nolog | No creation log message | True for silent creates |
| mail_notrack | Disable field tracking | True for batch writes |
| import_compat | Import mode flag | True during data import |
| force_company | Force specific company | company_id integer |
Default Values via Context
The most common use — pre-fill fields when creating records:
# In Python
self.with_context(
default_type='out_invoice',
default_partner_id=customer.id,
default_journal_id=journal.id,
).env['account.move'].create({})
# In XML action
<field name="context">{
'default_type': 'out_invoice',
'default_partner_id': active_id,
}</field>Any context key starting with default_ automatically sets the corresponding field's default value during create.
Language Switching
# Get product name in French
product_fr = product.with_context(lang='fr_FR')
french_name = product_fr.name
# Write translated value
product.with_context(lang='fr_FR').write({
'name': 'Nom en français',
})
# Send email in partner's language
partner_lang = partner.lang or 'en_US'
template.with_context(lang=partner_lang).send_mail(record.id)Controlling Mail Behavior
# Silent create — no chatter messages at all
record = self.with_context(
mail_create_nolog=True,
mail_create_nosubscribe=True,
tracking_disable=True,
).create(vals)
# Silent write — no tracking messages
record.with_context(tracking_disable=True).write({
'state': 'done',
})
# No auto-subscribe on create
record = self.with_context(
mail_create_nosubscribe=True,
).create(vals)These are essential for bulk imports and data migration where thousands of tracking messages would slow everything down.
Active Records Filtering
# Default: only active records
active_partners = self.env['res.partner'].search([])
# Include archived records
all_partners = self.env['res.partner'].with_context(
active_test=False,
).search([])
# Search specifically for archived
archived = self.env['res.partner'].with_context(
active_test=False,
).search([('active', '=', False)])Multi-Company Context
# Switch to a specific company's context
records = self.with_context(
allowed_company_ids=[target_company.id],
).env['sale.order'].search([])
# Access records from multiple companies
records = self.with_context(
allowed_company_ids=[company_a.id, company_b.id],
).env['sale.order'].search([])Custom Context Flags
Create your own context flags to control behavior:
# Set a flag
record.with_context(skip_validation=True).write(vals)
# Check the flag in a constraint or override
@api.constrains('amount')
def _check_amount(self):
if self.env.context.get('skip_validation'):
return
for record in self:
if record.amount < 0:
raise ValidationError("Amount must be positive.")Common Pitfalls
- Context is immutable —
with_context()returns a NEW recordset with the new context. The original is unchanged. You must use the returned value. - Context accumulation — Nested with_context calls accumulate keys. If a key is set upstream, it persists downstream unless explicitly overridden.
- Performance — Each with_context() call creates a new Environment object. Avoid calling it in tight loops.
- Security — Never trust context values from the client (they can be manipulated). Validate context flags in server-side code.
- active_test trap — Forgetting active_test=False when searching for archived records is a common source of "record not found" errors.