Skip to content

OD125 python-api-deprecated

The module uses a Python API its series deprecates or ignores.

Category Severity Series Needs the index
Deprecation Warning saas~12.5 and later no

What it reports

A form of the Python API that Odoo warns about in the module's series and still runs, from the series in the second column until the one in the third, where Odoo ignores the form, as the next table lists, or removes it, which OD126 reports:

Form Deprecated from Ignored or removed from Use instead
field option track_visibility= saas~13.4 saas~18.1, ignored tracking=
sudo(user) saas~12.5 saas~15.3 with_user(user)
an @api.onchange method returning domain saas~13.2 saas~16.3, ignored a domain on the field or in the view
flush(), recompute() on a recordset saas~15.4 saas~16.2 flush_model(), flush_recordset() or self.env.flush_all()
invalidate_cache() on a recordset saas~15.4 saas~16.2 invalidate_model(), invalidate_recordset() or self.env.invalidate_all()
refresh() on a recordset saas~15.2 saas~16.2 self.env.invalidate_all(), invalidate_cache() before saas~15.4
check_access_rights(), check_access_rule() 18.0 saas~19.1 check_access(), or has_access() for check_access_rights() given raise_exception=False
read_group() saas~18.2 saas~19.1, before 20.0 _read_group()
an override of read_group() in a model class saas~18.2 saas~19.1 an override of _read_group() or formatted_read_group()
an import of odoo.osv 19.0 saas~19.1 odoo.fields.Domain
@route(type='json') saas~18.1 none type='jsonrpc'

It reports a form that Odoo accepts and does nothing with from the series in the second column:

Form Ignored from Use instead
field option oldname= saas~12.5 an upgrade script that renames the column
the context key force_company, in with_context() saas~13.1 with_company(company)
field option track_visibility= saas~18.1 tracking=
an @api.onchange method returning domain saas~16.3 a domain on the field or in the view
an override of _name_search() in a model class 18.0 an override of _search_display_name()

sudo() counts as given a user when its argument is an integer, a ref() or browse() call with or without .id, or a name whose last word is user or uid, before any id, or superuser with the id, such as user, self.env.uid, admin_user.id or SUPERUSER_ID. A name whose first word is is, has or can is a flag, so sudo(is_user) is not given a user, and by their last word none of self.env.su, as_superuser or self.user_can_edit is. check_access_rights() passed False as raise_exception= or as its second argument only answers whether the user may, so the advice for it is has_access().

flush(), recompute(), invalidate_cache() and refresh() are reported on self in a model class and on a model the code names, such as self.env["res.partner"], since cursors, files and browsers have methods of these names. A call of any method in the table is not reported when the enclosing model class defines the method without calling the one it overrides through super(), and the call reaches that definition: on self, through sudo(), with_context() and the other methods that keep the model, or on the class's own model, such as self.env["res.users"] in a class of res.users. A reported override of read_group() covers the name of its super().read_group() calls, which is not reported again, while a keyword such a call passes still is. An import is reported once per statement, wherever it sits, so an import inside a function counts too. An onchange counts when it returns a literal dict with a domain key. force_company counts as a keyword of with_context() or a key of the literal dict it is given, so code that only reads the key from the context is not reported.

Why it matters

Each deprecated form still works in the series that deprecates it, and logs a DeprecationWarning or a warning in the server log. A later series removes it, and OD126 reports the same form as an error from then on, or keeps accepting it and ignores it. type='json' is an alias of jsonrpc, and the table gives no removal series for it.

Nothing fails on an ignored form, so the module loads and runs and quietly loses the behaviour. From saas~12.5 Odoo no longer renames the column of a field declared with oldname, so the values stay in the old column and the field starts empty, and from saas~18.1 a field with track_visibility and no tracking is no longer tracked in the chatter. saas~13.1 only logs force_company, so the records keep the current company, saas~16.3 drops the domain of an onchange, and 18.0 no longer calls an override of _name_search().

The 20.0 read_group() is another method with the name of the old one, so a call of it on 20.0 is not reported here. OD126 reports one that passes fields=, orderby= or lazy=, and an override of it, which 20.0 forbids.

Example

models/sale_order.py
class SaleOrder(models.Model):
    _inherit = "sale.order"

    def _confirm_as(self, user):
        self.check_access_rights("write")
        return self.with_user(user).action_confirm()

On 18.0, check_access_rights() warns on every call, and saas~19.1 no longer has it.

models/sale_order.py
class SaleOrder(models.Model):
    _inherit = "sale.order"

    def _confirm_as(self, user):
        self.check_access("write")
        return self.with_user(user).action_confirm()

Fixing

oscn check --fix rewrites sudo(user) given only the user to with_user(user), and track_visibility= given a non-empty string to tracking=True when the call passes neither tracking= nor track_sequence= and unpacks no dict with **. The other forms are left to rewrite by hand.

See fixing findings.

Suppressing

self.check_access_rights("write")  # odooscan: ignore[OD125]

See suppressing a finding.