When I first started developing with Odoo, I made a costly mistake that haunted me for weeks. I used update() instead of write() in a critical business process, and suddenly, none of our automated workflows were triggering. Orders weren’t being confirmed, emails weren’t sending, and our entire sales pipeline ground to a halt.
That painful experience taught me the fundamental truth about Odoo write vs update methods: they’re not interchangeable, and choosing the wrong one can break your entire application.
If you’re an Odoo developer who wants to avoid the same pitfall and build robust, reliable applications, this comprehensive guide will transform how you handle data modifications in Odoo.
Why Understanding Odoo Write vs Update Is Mission-Critical
The Odoo write vs update distinction isn’t just academic—it’s the difference between applications that work seamlessly and those that fail silently. Many developers assume these methods are equivalent, but this assumption leads to broken business rules, missed automations, and frustrated users.
Here’s what’s at stake:
- Business Logic Integrity: Using the wrong method can bypass critical validations
- Automation Reliability: Your carefully crafted workflows might never execute
- Data Consistency: Records can become inconsistent with business rules
- Debugging Nightmares: Silent failures are the hardest bugs to track down
The Complete Odoo Write vs Update Breakdown
Method 1: The write() Method – Your Safety Net
The write() method is Odoo’s recommended approach for data modification. Think of it as your application’s safety net—it ensures every business rule, validation, and automation runs exactly as intended.
What write() Does Behind the Scenes:
- Triggers Custom Overrides: Any custom
write()methods you’ve defined will execute - Activates Automations: Server actions, automated actions, and workflows fire correctly
- Enforces Constraints: All
@api.constrainsdecorators validate your data - Maintains Relationships: Related fields and computed fields update appropriately
- Logs Changes: Proper audit trails and change tracking occur
Step-by-Step Implementation:
class SaleOrder(models.Model):
_inherit = 'sale.order'
def action_confirm(self):
# CORRECT: Using write() for business logic
self.write({
'state': 'sale',
'date_order': fields.Datetime.now(),
'confirmation_user_id': self.env.user.id
})
# This triggers all related automations:
# - Email notifications
# - Inventory reservations
# - Accounting entries
# - Custom business rules
Method 2: The update() Method – Handle with Extreme Care
The update() method operates at a lower level, directly modifying the record’s cache without triggering the full Odoo machinery. It’s faster but dangerous when misused.
What update() Skips:
- Custom
write()method overrides - Automated actions and server actions
- Constraint validations
- Related field computations
- Change tracking and audit logs
When to Use update() (Rare Cases Only):
class DataMigrationWizard(models.TransientModel):
_name = 'data.migration.wizard'
def migrate_legacy_data(self):
# ACCEPTABLE: Mass data migration where speed matters
# and business logic should be bypassed
legacy_records = self.env['legacy.model'].search([])
for record in legacy_records:
record.update({
'migrated_flag': True,
'migration_date': fields.Datetime.now()
})
Real-World Scenarios: Odoo Write vs Update in Action
Scenario 1: E-commerce Order Processing
The Challenge: You need to update order status when payment is confirmed.
Wrong Approach (Using update()):
def confirm_payment(self):
# DANGEROUS: Bypasses all business logic
self.update({'payment_state': 'paid'})
# Result: No email sent, inventory not reserved, accounting not updated
Correct Approach (Using write()):
def confirm_payment(self):
# SAFE: Triggers all necessary business processes
self.write({
'payment_state': 'paid',
'payment_date': fields.Datetime.now()
})
# Result: Complete workflow execution
Scenario 2: Product Price Management
The Challenge: Implementing dynamic pricing with proper change tracking.
class ProductTemplate(models.Model):
_inherit = 'product.template'
def write(self, vals):
# Custom logic before standard write
if 'list_price' in vals:
old_price = self.list_price
self._create_price_history(old_price, vals['list_price'])
# Execute standard write (triggers all automations)
result = super().write(vals)
# Custom logic after write
if 'list_price' in vals:
self._notify_price_change_subscribers()
self._update_related_pricelists()
return result
Advanced Odoo Write vs Update Techniques
Technique 1: Conditional Business Logic
class StockMove(models.Model):
_inherit = 'stock.move'
def write(self, vals):
# Pre-write validation
if 'state' in vals and vals['state'] == 'done':
self._validate_move_completion()
result = super().write(vals)
# Post-write actions
if 'state' in vals:
self._trigger_state_dependent_actions(vals['state'])
return result
Technique 2: Batch Operations with Proper Validation
def bulk_update_with_validation(self, records, values):
"""
Safely update multiple records while maintaining business logic
"""
for record in records:
# Use write() to ensure each record's business logic executes
record.write(values)
# Alternative: Use recordset write for better performance
# records.write(values) # This also triggers all business logic
Performance Considerations in Odoo Write vs Update
When Performance Matters
While write() is safer, there are legitimate performance concerns in specific scenarios:
High-Volume Data Processing:
def process_large_dataset(self):
records = self.env['large.model'].search([])
# For 10,000+ records, consider batching
batch_size = 1000
for i in range(0, len(records), batch_size):
batch = records[i:i + batch_size]
# Still use write() but in smaller batches
batch.write({'processed': True})
Migration Scripts:
def migration_script(self):
# Only in migration contexts where business logic should be bypassed
records = self.env['legacy.model'].with_context(migration_mode=True).search([])
# Use update() only when you're certain no business logic is needed
for record in records:
record.update({'migrated': True})
Common Pitfalls and How to Avoid Them
Pitfall 1: Silent Automation Failures
Problem: Using update() in business logic causes automations to fail silently.
Solution: Always use write() for business operations and test automation thoroughly.
Pitfall 2: Inconsistent Data States
Problem: Bypassing constraints leads to invalid data combinations.
Solution: Implement comprehensive @api.constrains methods and use write() to enforce them.
Pitfall 3: Missing Audit Trails
Problem: update() doesn’t trigger change tracking, making debugging impossible.
Solution: Use write() to maintain proper audit logs and change history.
Testing Your Odoo Write vs Update Implementation
Unit Testing Best Practices
class TestWriteVsUpdate(TransactionCase):
def test_write_triggers_automation(self):
"""Test that write() properly triggers business logic"""
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
})
# Use write() and verify automation triggered
order.write({'state': 'sale'})
# Assert that related records were created/updated
self.assertTrue(order.picking_ids)
self.assertEqual(order.invoice_status, 'to invoice')
def test_update_bypasses_automation(self):
"""Test that update() bypasses business logic (when intended)"""
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
})
# Use update() and verify automation was bypassed
order.update({'state': 'sale'})
# Assert that related records were NOT created
self.assertFalse(order.picking_ids)
Best Practices for Odoo Write vs Update
Golden Rules to Follow
- Default to
write(): Unless you have a specific reason to bypass business logic, always usewrite() - Document
update()Usage: When you must useupdate(), document why business logic should be bypassed - Test Thoroughly: Verify that all expected automations and validations execute
- Monitor Performance: Use profiling tools to identify genuine performance bottlenecks
- Implement Proper Error Handling: Catch and handle validation errors appropriately
Code Review Checklist
- [ ] Is
write()used for all business logic operations? - [ ] Are there valid reasons for any
update()usage? - [ ] Do all custom
write()methods callsuper().write()? - [ ] Are constraint validations comprehensive?
- [ ] Is automation testing included?
Advanced Integration Patterns
Pattern 1: Multi-Model Coordination
class SaleOrder(models.Model):
_inherit = 'sale.order'
def write(self, vals):
result = super().write(vals)
# Coordinate with related models
if 'state' in vals and vals['state'] == 'sale':
self._update_related_projects()
self._notify_warehouse()
self._schedule_delivery()
return result
Pattern 2: External System Integration
def write(self, vals):
# Pre-write: Prepare external system data
external_data = self._prepare_external_sync(vals)
result = super().write(vals)
# Post-write: Sync with external systems
if external_data:
self._sync_with_external_system(external_data)
return result
Troubleshooting Odoo Write vs Update Issues
Debugging Checklist
- Verify Method Usage: Confirm you’re using
write()for business logic - Check Override Chain: Ensure all custom
write()methods callsuper() - Test Constraints: Verify
@api.constrainsmethods are working - Monitor Automations: Check that server actions and automated actions execute
- Review Logs: Examine Odoo logs for validation errors or exceptions
Common Error Messages and Solutions
Error: “ValidationError: Invalid field value”
Solution: Use write() to trigger proper constraint validation
Error: “AccessError: You cannot modify this record”
Solution: Check security rules and use sudo() if appropriate
Conclusion: Mastering Odoo Write vs Update
Understanding the Odoo write vs update distinction is crucial for building reliable, maintainable Odoo applications. The key takeaways are:
- Use
write()by default for all business logic operations - Reserve
update()for rare cases where bypassing business logic is intentional - Test thoroughly to ensure automations and validations work correctly
- Document your decisions when deviating from standard practices
By following these principles, you’ll build Odoo applications that are robust, predictable, and maintainable. Your future self (and your team) will thank you for taking the time to implement proper data modification patterns.
Remember: in the world of Odoo development, the safe choice is almost always the right choice. When in doubt, choose write() over update(), and your applications will reward you with reliable, consistent behavior.
For more advanced Odoo development techniques, explore the official Odoo documentation and consider joining the Odoo Community Association to stay updated with best practices and emerging patterns.
Ready to implement bulletproof Odoo applications? Start by auditing your existing code for update() usage and converting business logic operations to use write() instead. Your applications—and your users—will immediately benefit from more reliable, predictable behavior.
Discover more from teguhteja.id
Subscribe to get the latest posts sent to your email.

