Skip to content

feat(orm): --resolve-relation CLI flag + ORM-minimizing docs#203

Merged
bosd merged 3 commits into
masterfrom
feat/resolve-relations-cli
Jun 11, 2026
Merged

feat(orm): --resolve-relation CLI flag + ORM-minimizing docs#203
bosd merged 3 commits into
masterfrom
feat/resolve-relations-cli

Conversation

@bosd

@bosd bosd commented Jun 11, 2026

Copy link
Copy Markdown
Member

Fills the two gaps found while auditing the (already-implemented) ORM-minimizing load path.

Context

The ORM-minimizing feature is essentially built: export_id_map, Processor.resolve_relation, the resolve_relations engine hook, the vectorized Polars anti-join in idempotent.py, --auto-clean, and the test_orm_minimizing.py acceptance e2e all exist. The e2e passes on real Odoo 18 (optimized-vs-naive parity, idempotency, timing — 21 e2e tests green). Two gaps remained:

1. --resolve-relation CLI flag

The resolve_relations engine hook (pre-resolve a relation in Polars so Odoo runs no name_search) was only reachable programmatically or from a transform script. This adds a repeatable CLI flag:

fluvo import --connection-file conf/connection.conf --file partners.csv \
    --model res.partner \
    --resolve-relation country:res.country:code:country_id

Format: source_column:model:key_field:relation_field[:xmlid|dbid]. Parsed into the resolve_relations spec list (_parse_resolve_relation_specs), with validation. Parser + CLI tests included.

2. Docs

The ORM-minimizing optimizations were implemented but undocumented (a docs audit found resolve-relation, auto-clean, skip-unchanged, the name_search-avoidance rationale all at 0 doc files). Added a "Minimizing Odoo's ORM Work" section to the performance-tuning guide covering pre-resolving relations, --skip-unchanged, side-effect suppression, and --auto-clean.

No behavior change to existing imports — the flag is additive and opt-in.

The resolve_relations engine hook (pre-resolve relations in Polars so Odoo skips
name_search) was only reachable programmatically / via a transform script. Add a
repeatable --resolve-relation flag (source_column:model:key_field:relation_field
[:xmlid|dbid]) that parses into the resolve_relations spec list.

Also document the ORM-minimizing optimizations (pre-resolve relations,
--skip-unchanged anti-join, side-effect suppression, --auto-clean) in the
performance-tuning guide — they were implemented but undocumented.

Parser + CLI tests included.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new --resolve-relation CLI option to pre-resolve relational columns in Polars before loading data into Odoo, minimizing Odoo's ORM work. It includes documentation updates, a parser helper, and unit tests. The review feedback suggests improving the parser validation to reject specs with empty fields (e.g., country::code:country_id) and adding corresponding test coverage for this validation.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/fluvo/__main__.py Outdated
Comment on lines +51 to +55
if len(parts) not in (4, 5):
raise click.BadParameter(
f"--resolve-relation {raw!r}: expected "
"'source_column:model:key_field:relation_field[:xmlid|dbid]'."
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The parser currently only checks the number of colon-separated parts, but does not verify if any of the required fields (the first four parts) are empty (e.g., country::code:country_id). If an empty string is parsed for the model or key field, it will lead to failures or unexpected behavior later during the Polars join or Odoo query. We should validate that all required parts are non-empty.

        if len(parts) not in (4, 5) or not all(parts[:4]):
            raise click.BadParameter(
                f"--resolve-relation {raw!r}: expected "
                "'source_column:model:key_field:relation_field[:xmlid|dbid]' "
                "with non-empty fields."
            )

Comment on lines +43 to +46
def test_parse_rejects_wrong_part_count() -> None:
"""A spec with too few parts is rejected."""
with pytest.raises(click.BadParameter):
_parse_resolve_relation_specs(("country:res.country:code",))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To ensure that the new validation for empty fields in the --resolve-relation spec is thoroughly tested, we should add a test case that verifies that specs with empty fields are rejected.

Suggested change
def test_parse_rejects_wrong_part_count() -> None:
"""A spec with too few parts is rejected."""
with pytest.raises(click.BadParameter):
_parse_resolve_relation_specs(("country:res.country:code",))
def test_parse_rejects_wrong_part_count() -> None:
"""A spec with too few parts is rejected."""
with pytest.raises(click.BadParameter):
_parse_resolve_relation_specs(("country:res.country:code",))
def test_parse_rejects_empty_fields() -> None:
"""A spec with empty fields is rejected."""
with pytest.raises(click.BadParameter):
_parse_resolve_relation_specs(("country::code:country_id",))

Validate the 4 required parts are non-empty (e.g. 'country::code:country_id'
was accepted before), with a test.
@bosd

bosd commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Both applied: the parser now rejects specs with any empty required field (not all(parts[:4])), with a test_parse_rejects_empty_fields test. Thanks.

@bosd

bosd commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

This is already in — test_parse_rejects_empty_fields (asserting country::code:country_id raises BadParameter) was added in this same commit (3b49872) alongside the not all(parts[:4]) validation. No further change needed.

…ons-cli

# Conflicts:
#	src/fluvo/__main__.py
@bosd bosd merged commit ef4390b into master Jun 11, 2026
@bosd bosd deleted the feat/resolve-relations-cli branch June 11, 2026 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant