Volver al blog
GuideOdoo 18Migration

Complete Guide: How to Migrate Odoo 17 to 18

Step-by-step guide to migrate your custom Odoo modules from version 17 to 18. Covers Python API changes, XML view updates, JavaScript/OWL migration, and automated tools.

Equipo de MigrateFelix 25 de marzo de 2026 12 min de lectura

Introduction

Migrating custom Odoo modules between versions is one of the most time-consuming tasks for Odoo developers and partners. With Odoo 18 introducing significant changes to views, Python APIs, and JavaScript components, a structured approach is essential.

This guide covers every major change between Odoo 17 and 18, with practical before/after code examples and tips for automating the process.

Major Changes in Odoo 18

1. Tree Views Renamed to List Views

One of the most visible changes in Odoo 18 is the renaming of <tree> views to <list>. While <tree> still works as a deprecated alias, all new code should use <list>.

Before (Odoo 17):

<record id="view_partner_tree" model="ir.ui.view">
    <field name="name">res.partner.tree</field>
    <field name="model">res.partner</field>
    <field name="arch" type="xml">
        <tree string="Partners">
            <field name="name"/>
            <field name="email"/>
        </tree>
    </field>
</record>

After (Odoo 18):

<record id="view_partner_tree" model="ir.ui.view">
    <field name="name">res.partner.list</field>
    <field name="model">res.partner</field>
    <field name="arch" type="xml">
        <list string="Partners">
            <field name="name"/>
            <field name="email"/>
        </list>
    </field>
</record>

2. attrs Attribute Removed

The attrs attribute that was used for dynamic visibility, readonly, and required conditions has been completely removed. Instead, use direct attributes on the field elements.

Before (Odoo 17):

<field name="partner_id"
    attrs="{'invisible': [('state', '=', 'draft')],
            'required': [('state', '=', 'confirmed')]}"/>

After (Odoo 18):

<field name="partner_id"
    invisible="state == 'draft'"
    required="state == 'confirmed'"/>

3. name_get Replaced by _compute_display_name

The name_get method has been deprecated in favor of the computed field _compute_display_name.

Before (Odoo 17):

def name_get(self):
    result = []
    for rec in self:
        name = f"[{rec.code}] {rec.name}"
        result.append((rec.id, name))
    return result

After (Odoo 18):

@api.depends('code', 'name')
def _compute_display_name(self):
    for rec in self:
        rec.display_name = f"[{rec.code}] {rec.name}"

4. track_visibility Replaced by tracking

The track_visibility parameter on fields has been replaced by tracking.

Before (Odoo 17):

name = fields.Char(track_visibility='onchange')
state = fields.Selection([...], track_visibility='always')

After (Odoo 18):

name = fields.Char(tracking=True)
state = fields.Selection([...], tracking=True)

5. OWL Component Changes

Odoo 18 continues the migration to OWL 2. Key changes include updated lifecycle hooks and component registration.

Before (Odoo 17):

const { Component, useState } = owl;
class MyComponent extends Component {
    setup() {
        this.state = useState({ value: 0 });
    }
}
MyComponent.template = 'my_module.MyComponent';

After (Odoo 18):

import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
class MyComponent extends Component {
    static template = 'my_module.MyComponent';
    setup() {
        this.state = useState({ value: 0 });
    }
}
registry.category("actions").add("my_action", MyComponent);

6. Manifest Version Format

The version in __manifest__.py must be updated to reflect the target Odoo version.

Before: 'version': '17.0.1.0.0'

After: 'version': '18.0.1.0.0'

Additional Changes to Watch

  • column_invisible — now uses direct column_invisible attribute instead of being inside attrs
  • Security CSV files — Group references must be fully qualified (e.g., base.group_user not just group_user)
  • website.published_button template changes for website modules
  • Mail thread mixins — _track_subtype renamed to _track_template
  • Report actions — report.paperformat_id handling updated

Automate Your Migration with MigrateFelix

Manually updating every file in your module is error-prone and time-consuming. MigrateFelix automates the repetitive migration work using 62 deterministic rules combined with AI analysis.

How it works:

  1. Upload your custom module as a ZIP file
  2. Select source version (17) and target version (18)
  3. MigrateFelix analyzes every file — Python, XML, JavaScript, CSV
  4. Download your migrated module with a detailed report

The entire process takes minutes instead of hours or days of manual work.

Try MigrateFelix free →

Migration Checklist

Use this checklist to verify your migration is complete:

  1. Update __manifest__.py version to 18.0.x.x.x
  2. Replace all <tree> with <list>
  3. Remove attrs and use direct domain attributes
  4. Replace name_get with _compute_display_name
  5. Replace track_visibility with tracking=True
  6. Update OWL components to OWL 2 syntax
  7. Verify security CSV group references are fully qualified
  8. Test all views, reports, and workflows
  9. Create a migration script if database schema changed

Conclusion

Migrating from Odoo 17 to 18 requires attention to several breaking changes, but with the right tools and a systematic approach, it can be done efficiently. Whether you do it manually using this guide or automate it with MigrateFelix, the key is to test thoroughly after migration.

Have questions about your specific migration scenario? Contact us — we're happy to help.

¿Listo para migrar tu módulo?

MigrateFelix automatiza el trabajo repetitivo. Sube tu ZIP y obtén resultados en minutos.

Prueba MigrateFelix gratis →