From cebf486f6f4feab4375011c30a40508809057394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 30 Jan 2023 14:54:07 +0100 Subject: [PATCH] Enforce unique creation of singleton with lock and constraint Closes #10748 --- trytond/trytond/ir/message.xml | 3 +++ trytond/trytond/model/modelsingleton.py | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/trytond/trytond/ir/message.xml b/trytond/trytond/ir/message.xml index 048cea8b12..e09910d8e5 100644 --- a/trytond/trytond/ir/message.xml +++ b/trytond/trytond/ir/message.xml @@ -136,6 +136,9 @@ this repository contains the full copyright notices and license terms. --> ID must be positive. + + Only one singleton can be created. + You are not allowed to modify the field "%(field)s" in "%(record)s" of "%(model)s". diff --git a/trytond/trytond/model/modelsingleton.py b/trytond/trytond/model/modelsingleton.py index 04b7351af7..ee8ec2d241 100644 --- a/trytond/trytond/model/modelsingleton.py +++ b/trytond/trytond/model/modelsingleton.py @@ -1,8 +1,11 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. +from sql.operators import Equal + from trytond.transaction import Transaction, without_check_access +from .modelsql import Exclude, ModelSQL from .modelstorage import ModelStorage @@ -17,6 +20,12 @@ class ModelSingleton(ModelStorage): # Cache disable because it is used as a read by the client cls.__rpc__['default_get'].cache = None + if issubclass(cls, ModelSQL): + table = cls.__table__() + cls._sql_constraints.append( + ('singleton', Exclude(table, (table.id * 0, Equal)), + 'ir.msg_singleton')) + @classmethod def get_singleton(cls): ''' @@ -31,6 +40,8 @@ class ModelSingleton(ModelStorage): assert len(vlist) == 1 singleton = cls.get_singleton() if not singleton: + if issubclass(cls, ModelSQL): + cls.lock() return super(ModelSingleton, cls).create(vlist) cls.write([singleton], vlist[0]) return [singleton]