[FIX] orm: don't log errors on constraint failure unless necessary

Given an scenario in which a field F of model M is defined in module X
as `required=True` and is extended by another module Y as
`required=False` in a database with data not satisfying the original
constraint:

During an upgrade of base the original constraint will be re-applied
on the data (and fail) even though it is no longer necessary because
module Y relaxes the NOT NULL constraint.

This failure in and of itself is non-blocking, the upgrade will go
through but an error and a warning are logged anyway which are not
problematic either except in the case of automated testing
infrastructure (such as runbot), because of this it would be best if
these errors would not be logged at all unless we're 100% sure that the
constraint that was applied is not relaxed downstream.

With this commit, the `finalize_constraints` method will verify that the
constraint is applicable (field is required) before re-applying the NOT
NULL constraint.

opw-2269220

closes odoo/odoo#53529

X-original-commit: f09f4826fbdd1a547503c51a3cfad71285312c49
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
This commit is contained in:
Adrian Torres
2020-06-23 15:33:04 +00:00
committed by Raphael Collet
parent 195c7e83fe
commit ce4fcebbbb
+11 -1
View File
@@ -836,7 +836,7 @@ class Field(MetaField('DummyField', (object,), {})):
def add_not_null():
# flush values before adding NOT NULL constraint
model.flush([self.name])
model.pool.post_constraint(sql.set_not_null, model._cr, model._table, self.name)
model.pool.post_constraint(apply_required, model, self.name)
elif not self.required and has_notnull:
sql.drop_not_null(model._cr, model._table, self.name)
@@ -3702,6 +3702,16 @@ def prefetch_x2many_ids(record, field):
return unique(id_ for ids in ids_list for id_ in ids)
def apply_required(model, field_name):
""" Set a NOT NULL constraint on the given field, if necessary. """
# At the time this function is called, the model's _fields may have been reset, although
# the model's class is still the same. Retrieve the field to see whether the NOT NULL
# constraint still applies
field = model._fields[field_name]
if field.store and field.required:
sql.set_not_null(model.env.cr, model._table, field_name)
# imported here to avoid dependency cycle issues
from .exceptions import AccessError, MissingError, UserError
from .models import check_pg_name, BaseModel, NewId, IdType