Use creation position to format warning key for new records

The opposite position in the creation order is used instead of the id (which is
not stable over transaction retries) to format the warning key when the record
has been created during the transaction.
This ensures that the formatting of the warning key is stable over transaction
retries.

Closes #10591
This commit is contained in:
Cédric Krier committed 2025-11-27 16:26:20 +01:00
1 parent cd223660ff
commit 476065cf11
6 files changed
+37 -4

No files matched your search

+8
View File
@@ -40,8 +40,16 @@ Transaction
.. attribute:: Transaction.create_records
A :py:class:`defaultdict <collections.defaultdict>` storing for each
:attr:`~trytond.model.Model.__name__` a :py:class:`list` of ids created
during the transaction.
.. attribute:: Transaction.delete_records
A :py:class:`defaultdict <collections.defaultdict>` storing for each
:attr:`~trytond.model.Model.__name__` a :py:class:`set` of ids deleted
during the transaction.
.. attribute:: Transaction.trigger_records
.. attribute:: Transaction.check_warnings
+1 -1
View File
@@ -2312,7 +2312,7 @@ class ModelSQL(ModelStorage):
def lock(cls, records=None):
transaction = Transaction()
if records is not None:
new_ids = transaction.create_records[cls.__name__]
new_ids = set(transaction.create_records[cls.__name__])
ids = [id_ for id_ in map(int, records) if id_ not in new_ids]
transaction.lock_records(cls._table, ids)
else:
+1 -1
View File
@@ -299,7 +299,7 @@ class ModelStorage(Model):
for trigger in triggers:
trigger.queue_trigger_action(records)
transaction.create_records[cls.__name__].update(ids)
transaction.create_records[cls.__name__].extend(ids)
return ids
@classmethod
+11 -1
View File
@@ -964,7 +964,17 @@ class Warning_(ModelSQL, ModelView):
@classmethod
def format(cls, name, records):
key = '|'.join(map(str, records)).encode('utf-8')
create_records = Transaction().create_records
def str_(record):
name = record.__name__
try:
# start from 1 to avoid 0 == -0
i = create_records[name].index(record.id) + 1
return '%s,%s' % (name, -i)
except ValueError:
return str(record)
key = '|'.join(map(str_, records)).encode('utf-8')
return '%s.%s' % (hashlib.md5(key).hexdigest(), name)
@classmethod
+15
View File
@@ -82,5 +82,20 @@ class ResTestCase(ModuleTestCase):
self.assertFalse(Warning_.check('test'))
self.assertFalse(Warning_.check('test'))
@with_transaction()
def test_user_warning_format(self):
"Test format of user warning"
pool = Pool()
Warning_ = pool.get('res.user.warning')
Record = pool.get('res.user')
transaction = Transaction()
key1 = Warning_.format('test', [Record(1)])
transaction.create_records[Record.__name__].append(1)
key2 = Warning_.format('test', [Record(1)])
self.assertNotEqual(key1, key2)
del ModuleTestCase
+1 -1
View File
@@ -197,7 +197,7 @@ class Transaction(object):
self.readonly = readonly
self.close = close
self.context = ImmutableDict(context or {})
self.create_records = defaultdict(set)
self.create_records = defaultdict(list)
self.delete_records = defaultdict(set)
self.trigger_records = defaultdict(set)
self.log_records = []