1010import base64
1111import inspect
1212import os
13- from typing import Any , Callable , cast
13+ from typing import Any , Callable , Optional , cast
1414
1515import requests # type: ignore[import-untyped]
1616
5353LineDict = dict [str , Any ]
5454StateDict = dict [str , Any ]
5555MapperFunc = Callable [[LineDict , StateDict ], Any ]
56-
57-
58- # ... (rest of the file remains the same as your version) ...
56+ ListMapperFunc = Callable [[LineDict , StateDict ], list [str ]]
5957
6058
6159def _get_field_value (line : LineDict , field : str , default : Any = "" ) -> Any :
@@ -402,7 +400,9 @@ def m2o_att_fun(line: LineDict, state: StateDict) -> dict[str, str]:
402400 return m2o_att_fun
403401
404402
405- def m2m_id_list (prefix : str , * fields : Any , sep : str = "," ) -> MapperFunc :
403+ def m2m_id_list (
404+ prefix : str , * fields : Any , sep : str = "," , const_values : Optional [list [str ]] = None
405+ ) -> ListMapperFunc :
406406 """Returns a mapper for creating a list of M2M external IDs.
407407
408408 This is primarily used when creating the related records for a M2M field,
@@ -412,44 +412,63 @@ def m2m_id_list(prefix: str, *fields: Any, sep: str = ",") -> MapperFunc:
412412 prefix: The XML ID prefix to apply to each value.
413413 *fields: One or more source fields to read values from.
414414 sep: The separator to use when splitting values.
415+ const_values: A list of constant XML IDs to always include.
415416
416417 Returns:
417- A mapper function that returns a comma-separated string of external IDs.
418+ A mapper function that returns a list of individual external IDs.
418419 """
419- concat_m = concat ("" , * fields )
420+ if const_values is None :
421+ const_values = []
420422
421- def m2m_id_list_fun (line : LineDict , state : StateDict ) -> str :
422- value = concat_m (line , state )
423- if not value :
424- return ""
425- values = [v .strip () for v in value .split (sep )]
426- return "," .join (to_m2o (prefix , v ) for v in values if v )
423+ def m2m_id_list_fun (line : LineDict , state : StateDict ) -> list [str ]:
424+ # Call a shared internal helper to get unique, ordered raw values
425+ raw_values = _get_unique_raw_values_from_fields (
426+ line , state , fields , sep , const_values
427+ )
428+ # Apply prefixing to each raw value
429+ return [to_m2o (prefix , v ) for v in raw_values if v ]
427430
428431 return m2m_id_list_fun
429432
430433
431- def m2m_value_list (* fields : Any , sep : str = "," ) -> MapperFunc :
432- """Returns a mapper that creates a Python list of unique values.
434+ def m2m_value_list (
435+ * fields : Any , sep : str = "," , const_values : Optional [list [str ]] = None
436+ ) -> ListMapperFunc : # Changed to ListMapperFunc
437+ """Returns a mapper that creates a Python list of unique values."""
438+ if const_values is None :
439+ const_values = []
433440
434- This is used in conjunction with `m2m_id_list` when creating related
435- records for a M2M field.
441+ def m2m_value_list_fun (line : LineDict , state : StateDict ) -> list [str ]:
442+ # Call the same shared internal helper to get unique, ordered raw values
443+ raw_values = _get_unique_raw_values_from_fields (
444+ line , state , fields , sep , const_values
445+ )
446+ # Return the raw values directly
447+ return [v for v in raw_values if v ]
436448
437- Args:
438- *fields: One or more source fields to read values from.
439- sep: The separator to use when splitting values.
449+ return m2m_value_list_fun
440450
441- Returns:
442- A mapper function that returns a list of strings.
443- """
451+
452+ def _get_unique_raw_values_from_fields (
453+ line : LineDict , state : StateDict , fields : Any , sep : str , const_values : list [str ]
454+ ) -> list [str ]:
455+ """Helper to get unique, ordered raw values from fields and const_values."""
456+ all_raw_values : list [str ] = []
444457 concat_m = concat ("" , * fields )
445458
446- def m2m_value_list_fun (line : LineDict , state : StateDict ) -> list [str ]:
447- value = concat_m (line , state )
448- if not value :
449- return []
450- return [v .strip () for v in value .split (sep ) if v .strip ()]
459+ value_from_fields = concat_m (line , state )
460+ if value_from_fields :
461+ # Split and extend with values from fields
462+ all_raw_values .extend (
463+ [v .strip () for v in value_from_fields .split (sep ) if v .strip ()]
464+ )
451465
452- return m2m_value_list_fun
466+ # Extend with constant values
467+ all_raw_values .extend ([v .strip () for v in const_values if v .strip ()])
468+
469+ # Preserve order and ensure uniqueness using dict.fromkeys (Python 3.7+)
470+ unique_ordered_values = list (dict .fromkeys (all_raw_values ))
471+ return unique_ordered_values
453472
454473
455474def map_val (
0 commit comments