Files
tradon/model/modelsingleton.py
2025-12-26 13:11:43 +00:00

133 lines
4.5 KiB
Python
Executable File

# 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
class ModelSingleton(ModelStorage):
"""
Define a singleton model in Tryton.
"""
@classmethod
def __setup__(cls):
super().__setup__()
# 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):
'''
Return the instance of the unique record if there is one.
'''
singletons = super(ModelSingleton, cls).search([], limit=1)
if singletons:
return singletons[0]
@classmethod
def create(cls, vlist):
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]
@classmethod
def read(cls, ids, fields_names):
singleton = cls.get_singleton()
if not singleton:
fname_no_rec_name = [
f for f in fields_names
if '.' not in f and not f.startswith('_')]
res = cls.default_get(fname_no_rec_name,
with_rec_name=len(fname_no_rec_name) != len(fields_names))
for field_name in fields_names:
if field_name not in res:
res[field_name] = None
res['id'] = ids[0]
res['_write'] = True
res['_delete'] = True
return [res]
res = super(ModelSingleton, cls).read([singleton.id], fields_names)
res[0]['id'] = ids[0]
return res
@classmethod
def write(cls, records, values, *args):
singleton = cls.get_singleton()
if not singleton:
with without_check_access():
singleton, = cls.create([values])
actions = (records, {}) + args
else:
actions = (records, values) + args
args = []
for values in actions[1:None:2]:
args.extend(([singleton], values))
super(ModelSingleton, cls).write(*args)
# Clean local cache of original records
for record in sum(actions[0:None:2], []):
record._local_cache.pop(record.id, None)
# Clean transaction cache of all ids
for cache in Transaction().cache.values():
if cls.__name__ in cache:
cache[cls.__name__].clear()
@classmethod
def delete(cls, records):
singleton = cls.get_singleton()
if singleton:
super(ModelSingleton, cls).delete([singleton])
# Clean transaction cache of all ids
for cache in Transaction().cache.values():
if cls.__name__ in cache:
cache[cls.__name__].clear()
@classmethod
def copy(cls, records, default=None):
if default:
cls.write(records, default)
return records
@classmethod
def search(cls, domain, offset=0, limit=None, order=None, count=False):
res = super(ModelSingleton, cls).search(domain, offset=offset,
limit=limit, order=order, count=count)
if not res and not domain:
if count:
return 1
return [cls(1)]
return res
@classmethod
def default_get(cls, fields_names, with_rec_name=True):
if '_timestamp' in fields_names:
fields_names = list(fields_names)
fields_names.remove('_timestamp')
default = super(ModelSingleton, cls).default_get(fields_names,
with_rec_name=with_rec_name)
singleton = cls.get_singleton()
if singleton:
if with_rec_name:
fields_names = fields_names[:]
for field in fields_names[:]:
if cls._fields[field]._type in ('many2one',):
fields_names.append(field + '.rec_name')
default, = cls.read([singleton.id], fields_names=fields_names)
del default['id']
return default