Repository navigation
Fix post-merge review findings on has-one-of-many - #334
Merged
Merged
Conversation
- Ignore NULL columns in the of-many subquery so latest/oldest follow MAX/MIN semantics on every database - of_many() defaults to the related primary key and orders by it once - Type the aggregate as Literal["max", "min"] - Reject @relationship methods returning an existing descriptor or a builder, and HasMany relations without a resolvable foreign key - Rename relationships/relationship.py to RelationshipMethod.py while keeping the public relationship imports - Cover NULLs, custom primary keys, subclasses, self-referencing tables and MySQL/PostgreSQL SQL - Mark ADR 050 accepted and document the MySQL 1093 and naming caveats Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tmgbedu
commented
Oct 31, 2026
tmgbedu
left a comment
Contributor
Author
There was a problem hiding this comment.
Review of #334 (has-one-of-many post-merge fixes)
Verdict: approve with minor follow-ups. No blockers.
I checked out fix/has-one-of-many-review and ran uv run pytest --ignore=tests/masoniteorm/postgres --cov: 3647 passed, total coverage 92.64%, with HasOneOfMany.py and RelationshipMethod.py at 100%. The MySQL and Postgres SQL snapshot tests also pass without a database server, and ruff check is clean.
What I checked
- M1 (NULL ordering): I generated SQL with the Postgres and MySQL grammars for the eager,
where_hasandoldest_of_manyqueries. Each correlated subquery has<alias>.<column> IS NOT NULLbefore theORDER BY <column>, <pk>, so NULL rows are ignored the wayMAX/MINignore them. The new SQLite test covers a parent whose children all have NULL dates (it getsNone), for both directions. - M2 (custom primary key): with
__primary_key__ = "ticket_id",of_many()andof_many(aggregate="min")order only byticket_id, once, with no NULL filter. An explicitlatest_of_many("ticket_id")produces the same SQL. Postgres and MySQL both look right. - m1: returning
self.statuses, a builder, or another@relationshipresult raisesTypeError, and the shared descriptor keeps its name. - m3, m4, m5, m6, m7: all addressed. The
Literalaggregate type, the foreign-key check, therelated_model()test, the new tests (NULLs, custom pk, subclass, self-referencing eager andwhere_has) and the MySQL error 1093 caveat are all in place. - Repo rules: ADR 050 and its index entry are updated, and the PR adds no explanatory comments or docstrings.
Minor
- The old module path no longer imports, but the ADR says it does.
relationships/relationship.pyshipped in v0.71.0. After the rename,from fastapi_startkit.masoniteorm.relationships.relationship import relationship(orimport RelationshipMethodfrom it) raisesModuleNotFoundError.from fastapi_startkit.masoniteorm.relationships import relationshipstill works. Either add a small compatibility module at the old path, or change the ADR wording ("still exported as …relationships.relationship") to say the old module path was removed. - The ADR dropped a caveat that still applies. The earlier text said global scopes on the related model also run on the correlated subquery's builder, and scopes that hard-code the table name instead of using the builder's alias aren't handled. The code hasn't changed there, so please put that note back under Caveats.
Nit
- In
one_of_many, the return value ofquery.where_column(...)is discarded, and the code relies on the builder changing in place. That is correct, but chaining the calls like the rest of the method would read more clearly.
Not flagged, as agreed: the self/@classmethod forms, the created_at default, and inner ofMany constraints.
No code was pushed and nothing was merged.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
- Re-export relationship and RelationshipMethod from relationships/relationship.py so the old module path imports again; the package imports through it so its attribute stays the function - Restore the ADR caveat about global scopes that hard-code the table name inside the correlated subquery - Chain where_column in one_of_many Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eview # Conflicts: # docs/adr/AGENTS.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #331, addressing the post-merge review (#331 (comment)).
Changes
alias.column IS NOT NULL, giving MAX/MIN semantics on SQLite, MySQL and PostgreSQL. A parent whose children are all NULL getsNone.of_many(column=None)resolves to the related model's__primary_key__(custom pks work); when the column is the pk it is ordered once with no NULL filter.@relationshipraisesTypeErrorwhen the method returns an existing descriptor (return self.statuses) or a builder, leaving the shared descriptor intact.Literal["max", "min"](runtimeValueErrorkept).TypeErrorwhen the parentHasManyhas no explicitforeign_keyand no attribute name to derive one.model_referencecovered viarelated_model().to_sqlsnapshots (no live server needed).relationships/relationship.py→relationships/RelationshipMethod.py;fastapi_startkit.masoniteorm.relationshipandfastapi_startkit.masoniteorm.relationships.relationshipstill import.latest/oldestoverridesModel.latest()/oldest().Unchanged pending a user decision: both
selfand@classmethodforms, thecreated_atdefault forlatest_of_many/oldest_of_many, no innerofManyconstraints.Validation
HasOneOfMany.pyandRelationshipMethod.pyat 100%)langchainimport inai/ai.py)🤖 Generated with Claude Code