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]