Fix numeric natural keys resolving to the wrong asset on import
This commit is contained in:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user