From ce4fcebbbbaf9d2fb2c6e80870707679ab30d2bc Mon Sep 17 00:00:00 2001 From: Adrian Torres Date: Thu, 11 Jun 2020 14:17:50 +0000 Subject: [PATCH] [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) --- odoo/fields.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/odoo/fields.py b/odoo/fields.py index 854a2f831b2..8b7cbfe71cc 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -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