Skip to content

Commit a7db31a

Browse files
bosdbosd
authored andcommitted
fix(import): Handle tuple index out of range errors and external ID
conversion This commit fixes the 'tuple index out of range' errors that were occurring when importing data with external ID fields. The issue was caused by: 1. Incorrect filtering of external ID fields that prevented them from being processed for conversion 2. Improper handling of JSON-RPC exceptions that contained IndexError information 3. Missing conversion of external ID references to actual database IDs Changes made: - Allow external ID fields (ending with '/id') to pass through the initial filtering step - Add proper conversion of external ID references to database IDs before record creation - Handle empty external ID values correctly by converting them to False - Improve error handling for JSON-RPC exceptions that contain IndexError information - Add specific handling for 'tuple index out of range' errors in the import process - Add logging for debugging external ID conversion process These changes ensure that external ID fields are properly converted and that tuple index errors are gracefully handled, preventing crashes during import. Fixes #123
1 parent 5de7b50 commit a7db31a

File tree

1 file changed

+80
-3
lines changed

1 file changed

+80
-3
lines changed

src/odoo_data_flow/import_threaded.py

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ def __init__(
330330
)
331331

332332

333-
def _create_batch_individually(
333+
def _create_batch_individually( # noqa: C901
334334
model: Any,
335335
batch_lines: list[list[Any]],
336336
batch_header: list[str],
@@ -367,11 +367,64 @@ def _create_batch_individually(
367367
clean_vals = {
368368
k: v
369369
for k, v in vals.items()
370-
if "/" not in k and k.split("/")[0] not in ignore_set
370+
if k.split("/")[0]
371+
not in ignore_set # Allow external ID fields through for conversion
371372
}
372373

373374
# 3. CREATE
374-
new_record = model.create(clean_vals, context=context)
375+
# Convert external ID references to actual database IDs before creating
376+
converted_vals = {}
377+
external_id_fields = []
378+
for field_name, field_value in clean_vals.items():
379+
# Handle external ID references (e.g., 'parent_id/id' -> 'parent_id')
380+
if field_name.endswith("/id"):
381+
external_id_fields.append(field_name)
382+
base_field_name = field_name[:-3] # Remove '/id' suffix
383+
# Handle empty external ID references
384+
if not field_value:
385+
# Empty external ID means no value for this field
386+
converted_vals[base_field_name] = False
387+
log.debug(
388+
f"Converted empty external ID {field_name} -> "
389+
f"{base_field_name} (False)"
390+
)
391+
else:
392+
# Convert external ID to database ID
393+
try:
394+
# Look up the database ID for this external ID
395+
record_ref = model.browse().env.ref(
396+
field_value, raise_if_not_found=False
397+
)
398+
if record_ref:
399+
converted_vals[base_field_name] = record_ref.id
400+
log.debug(
401+
f"Converted external ID {field_name} "
402+
f"({field_value}) -> {base_field_name} "
403+
f"({record_ref.id})"
404+
)
405+
else:
406+
# If we can't find the external ID, set to False
407+
converted_vals[base_field_name] = False
408+
log.warning(
409+
f"Could not find record for external ID "
410+
f"'{field_value}' setting {base_field_name}"
411+
f" to False"
412+
)
413+
except Exception as e:
414+
log.warning(
415+
f"Error looking up external ID '{field_value}' "
416+
f"for field '{field_name}': {e}"
417+
)
418+
# On error, set to False
419+
converted_vals[base_field_name] = False
420+
else:
421+
# Regular field - pass through as-is
422+
converted_vals[field_name] = field_value
423+
424+
log.debug(f"External ID fields found: {external_id_fields}")
425+
log.debug(f"Converted vals keys: {list(converted_vals.keys())}")
426+
427+
new_record = model.create(converted_vals, context=context)
375428
id_map[source_id] = new_record.id
376429
except IndexError as e:
377430
error_message = f"Malformed row detected (row {i + 1} in batch): {e}"
@@ -380,7 +433,31 @@ def _create_batch_individually(
380433
error_summary = "Malformed CSV row detected"
381434
continue
382435
except Exception as create_error:
436+
# Handle JSON-RPC exceptions that contain IndexErrors
437+
error_str = str(create_error).lower()
438+
439+
# Check if this is a JSON-RPC exception containing an
440+
# IndexError/tuple index error
441+
if "tuple index out of range" in error_str or "indexerror" in error_str:
442+
error_message = f"Tuple unpacking error in row {i + 1}: {create_error}"
443+
failed_line = [*list(line), error_message]
444+
failed_lines.append(failed_line)
445+
if "Fell back to create" in error_summary:
446+
error_summary = "Tuple unpacking error detected"
447+
continue
448+
449+
# Handle other specific error types
383450
error_message = str(create_error).replace("\n", " | ")
451+
452+
# Handle "tuple index out of range" errors specifically
453+
if "tuple index out of range" in error_message:
454+
error_message = f"Tuple unpacking error in row {i + 1}: {error_message}"
455+
456+
# Handle invalid field errors (the new issue we discovered)
457+
elif "Invalid field" in error_message and "/id" in error_message:
458+
error_message = "Invalid external ID field detected in row "
459+
f"{i + 1}: {error_message}"
460+
384461
failed_line = [*list(line), error_message]
385462
failed_lines.append(failed_line)
386463
if "Fell back to create" in error_summary:

0 commit comments

Comments
 (0)