Fix numeric natural keys resolving to the wrong asset on import

This commit is contained in:
2026-08-31 15:55:31 -04:00
parent f087a7a5b0
commit 7081ea5d41
3 changed files with 74 additions and 10 deletions

View File

@@ -427,12 +427,41 @@ class Resolver:
def resolve(self, tablename, idcolumn, value):
if value is None:
return None
# A number is taken at face value: exports round-trip, and some sites do
# know their ids.
if isinstance(value, int) or (isinstance(value, str) and value.strip().isdigit()):
# An int is an id. It cannot be anything else.
if isinstance(value, int):
return int(value)
label = _label_column(tablename)
# An ALL-DIGIT STRING IS AMBIGUOUS, and used to be taken at face value
# as an id. That is wrong wherever a site's natural keys are numeric,
# and this one numbers operations 0NNN: assetnumber `0600` was read as
# id 600, which is a different asset - a printer, in the case that
# found this - or none at all, which surfaced as a foreign-key error
# partway through an Azure device import.
#
# The label wins when one matches. Somebody writing `0600` in a column
# keyed on assetnumber means the asset called 0600, and silently
# binding a row to an unrelated record is far worse than the error.
# Fall through to the id reading only when no label matches, which
# keeps round-tripped exports and sites that do know their ids working.
if isinstance(value, str) and value.strip().isdigit():
if label:
key = (tablename, value.strip().lower())
if key in self._cache:
return self._cache[key]
table = db.metadata.tables[tablename]
row = db.session.execute(
table.select().where(
db.func.lower(db.func.trim(table.c[label]))
== value.strip().lower())
).first()
if row is not None:
found = getattr(row, idcolumn)
self._cache[key] = found
return found
return int(value)
if not label:
raise ValueError(
"cannot look up %s by name - give the numeric %s instead" % (tablename, idcolumn))