AI Automation

Explore

27 Odoo
Anti-Patterns.

Critical mistakes that break enterprise projects. Learn to identify and avoid the most common Odoo development and configuration pitfalls.

01 / Safety

Python & ORM Anti-Patterns

#01

Writing Inside a Loop

Calling write() on individual records inside a loop generates N SQL queries.

Impact Analysis

Updating 10,000 records takes 30 seconds instead of less than 1 second.

❌ Bad Pattern
for record in records:
    record.write({'note': 'X'})
✅ Good Pattern
records.write({'note': 'X'})
#02

search() + filtered() Abuse

Fetching ALL records into memory then filtering in Python instead of the DB.

Impact Analysis

High memory usage and slow response time on large tables.

❌ Bad Pattern
all = env['res.partner'].search([])
res = all.filtered(lambda p: p.is_us)
✅ Good Pattern
res = env['res.partner'].search([('is_us', '=', True)])
#03

Overriding create() Without super()

Forgetting to call super() breaks the inheritance chain and other installed modules.

Impact Analysis

Features from other modules silently stop working, leading to data corruption.

❌ Bad Pattern
def create(self, vals):
    vals['x'] = 1
    # Missing return super()...
✅ Good Pattern
@api.model_create_multi
def create(self, vals_list):
    return super().create(vals_list)
#04

Using self[0] Without ensure_one()

Assuming a recordset contains only one record without explicitly checking.

Impact Analysis

Silent errors where logic is applied to the first record of a set, ignoring others.

❌ Bad Pattern
def do_something(self):
    name = self[0].name
✅ Good Pattern
def do_something(self):
    self.ensure_one()
    name = self.name
#05

Hardcoding XML IDs

Referencing records by their external ID string instead of using references.

Impact Analysis

Code breaks if the external ID changes or the module is renamed.

❌ Bad Pattern
rec = self.env.ref('base.main_company')
✅ Good Pattern
# Prefer passing via context or using config parameters.
#06

Incorrect depends() on Compute Fields

Forgetting a dependency or including a field that doesn't trigger recalculation.

Impact Analysis

The compute field doesn't update when the data changes, showing stale info.

❌ Bad Pattern
@api.depends('name')
def _comp(self): ... # Missing 'partner_id'
✅ Good Pattern
@api.depends('name', 'partner_id.name')
02 / Safety

Critical Security Mistakes

#07

SQL Injection via f-strings

Building raw SQL queries with string formatting instead of parameters.

Impact Analysis

Total database compromise via SQL Injection. Sensitive data theft.

❌ Bad Pattern
cr.execute(f"SELECT * FROM p WHERE n = '{name}'")
✅ Good Pattern
cr.execute("SELECT * FROM p WHERE n = %s", (name,))
#08

Abusing sudo()

Using sudo() to bypass errors instead of fixing underlying access rights.

Impact Analysis

Data leaks: Normal users seeing or modifying data they shouldn't access.

❌ Bad Pattern
self.sudo().write({'state': 'done'})
✅ Good Pattern
# Fix ir.model.access.csv or record rules instead.
#09

Unprotected Controller Routes

Defining web routes with auth='none' or auth='public' without internal checks.

Impact Analysis

Anonymous users can trigger internal Python methods or view private data.

❌ Bad Pattern
@http.route('/my/data', auth='public')
✅ Good Pattern
@http.route('/my/data', auth='user')
#10

Storing Sensitive Data in Plaintext

Saving passwords or API keys in regular Char fields instead of encrypted fields.

Impact Analysis

If the DB is compromised, all third-party integration keys are exposed.

❌ Bad Pattern
api_key = fields.Char("Key")
✅ Good Pattern
# Use ir.config_parameter or encrypted fields.
03 / Safety

Frontend & UX Mistakes

#11

Modifying Core Templates with replace

Using xpath position='replace' on standard views.

Impact Analysis

Breaks other modules that try to xpath into the same elements.

❌ Bad Pattern
<xpath expr="//field[@name='x']" position="replace">
✅ Good Pattern
<xpath expr="//field[@name='x']" position="attributes">
#12

Heavy Logic in t-esc / t-out

Performing complex Python calls inside QWeb XML templates.

Impact Analysis

Slow page rendering because logic runs for every line in a report/view.

❌ Bad Pattern
<t t-esc="doc.get_complex_total()"/>
✅ Good Pattern
# Pre-calculate in Python and pass to the template.
#13

Using jQuery in OWL Components

Directly manipulating the DOM with jQuery instead of OWL's reactive state.

Impact Analysis

UI becomes out of sync with the component state, causing random bugs.

❌ Bad Pattern
$('#my_div').hide()
✅ Good Pattern
this.state.isVisible = false
#14

Missing Responsive Classes

Designing views that only look good on 1920x1080 screens.

Impact Analysis

The mobile app and tablet views become unusable for on-site staff.

❌ Bad Pattern
<div style="width: 1200px">
✅ Good Pattern
<div className="col-12 col-md-6">
#15

Forgetting the 'String' on Fields

Relying on Odoo to auto-format technical field names as labels.

Impact Analysis

The UI shows 'X_partner_id' instead of 'Customer Name', looking unprofessional.

❌ Bad Pattern
my_field = fields.Char()
✅ Good Pattern
my_field = fields.Char("Customer Name")
05 / Safety

Performance Killers

#16

N+1 Query in Compute Fields

Accessing relational fields in a loop without prefetching.

Impact Analysis

Extremely slow form loads and list views (100+ queries per page).

❌ Bad Pattern
for r in self:
    r.val = r.partner_id.name # 1 query per r
✅ Good Pattern
self.mapped('partner_id')
for r in self:
    r.val = r.partner_id.name # Uses cache
#17

Abusing api.onchange for Logic

Placing heavy business logic in UI-only onchange methods.

Impact Analysis

The UI 'freezes' while typing because the server is processing complex data.

❌ Bad Pattern
@api.onchange('x')
def _check_all_db(self): ...
✅ Good Pattern
# Use @api.depends for data logic.
#18

Missing Database Indexes

Frequently searched fields without index=True.

Impact Analysis

Full table scans: Odoo gets slower as the DB grows.

❌ Bad Pattern
ref = fields.Char("Ref")
✅ Good Pattern
ref = fields.Char("Ref", index=True)
#19

Large Binary Fields in the DB

Storing 10MB+ PDFs/Images directly in the SQL table.

Impact Analysis

Slow DB backups and high memory usage. Use the filestore instead.

❌ Bad Pattern
file = fields.Binary(attachment=False)
✅ Good Pattern
file = fields.Binary(attachment=True)
#20

Unoptimized Cron Intervals

Running heavy data processing every 1 minute.

Impact Analysis

Database lock contention and slow performance for all users.

❌ Bad Pattern
interval_number: 1, interval_type: 'minutes'
✅ Good Pattern
# Schedule during off-peak hours (e.g., nightly).
06 / Safety

Infrastructure & Deployment

21. workers = 0

Running single-threaded in production. One slow user blocks every other user from using Odoo.

Fix: Set workers = (Cores * 2) + 1

22. No Filestore Backup

Restoring DB without /var/lib/odoo/filestore/. All images, PDFs, and attachments are lost forever.

Fix: Backup SQL and Filestore together.

23. Exposed Port 8069

Exposing Odoo directly to the internet without Nginx. No SSL, no rate limits, no buffer.

Fix: Use Nginx Reverse Proxy with SSL.
07 / Safety

Project Management Failures

24. Go-Live on Friday

Launching a new ERP system before the weekend when support staff is unavailable.

Fix: Go-Live on Monday/Tuesday morning.

25. Skipping UAT

Assuming the 'Consultant' tested everything without involving the actual business users.

Fix: Mandatory User Acceptance Testing.

26. The 'Big Bang' Migration

Trying to move 10 years of messy history into Odoo in one day without a pilot run.

Fix: Phased migration or Pilot testing.

27. Training via Manuals

Giving users a 200-page PDF and expecting them to learn Odoo. They won't read it.

Fix: Hands-on, Role-based training.

The 7 Golden Rules

  • 01If it works in Standard, don't customize it.
  • 02If you must customize, make it upgrade-safe.
  • 03If you use sudo(), document why in a comment.
  • 04If you write SQL, use parameterized queries (%s).
  • 05If you write in a loop, you're probably doing it wrong.
  • 06If you skip testing, you'll test in production.
  • 07If you don't backup the filestore, you don't have a backup.
DataDaur Code Audit & Rescue

Is Your Current Odoo System
Slow, Buggy, or Custom-Heavy?

Our certified Odoo architects audit legacy codebases to identify N+1 query loops, SQL bottlenecks, and security gaps. We rescue failing implementations and restore peace of mind.