Skip to main content

Module relation_has_many_via

Module relation_has_many_via 

Source
Expand description

Multi-step (via) has_many relations: a has_many reached by following a path of existing relations rather than a single foreign key.

The shape under test is UserCommentArticle: a user has many comments, each comment belongs to an article, so a user has many commented_articles via comments.article.

Functions§

eager_scalar_via_auto_loads
A non-deferred scalar via (tag_names: Vec<String>, no Deferred) is an eager relation edge: querying the parent auto-loads the projected terminal values without an explicit .include(). Every other via scenario wraps the field in Deferred, so this is the only test exercising the ViaManyField for Vec<E> (DEFERRED = false) impl and via auto-loading. The load groups per user and collapses duplicate values, like the explicit .include() paths.
filter_parent_by_via_any
.any() on a via field filters parent records through the expanded relation path. The same predicate works when the path contains another via field.
include_via_has_one
.include() of a has_one (single-result) via relation: UserAccountSubscription, both steps has_one. The via target is a single record, so this exercises the query.single branch of via-include lowering that the all-has_many scenarios never reach. The INNER JOIN drops a parent whose chain is incomplete at either step, so a missing leaf and a missing intermediate both surface as None.
include_via_nested_via
.include() over a via-of-via: User::nested_todos reaches todos through organizations.todos, where Organization::todos is itself a via. The outer path’s second step expands into a nested via during lowering, so this exercises recursive via flattening. The result must match the flat 3-step User::todos include in include_via_three_step exactly — same data shape, same expected grouping.
include_via_three_step
.include() over a 3-step via: User → Organization → Project → Todo, all HasMany steps. Verifies that the child query joins every intermediate and groups todos by the root user.
include_via_three_step_no_intermediates
A user with no intermediates yields an empty included set — the INNER JOIN excludes them but the parent row is still returned.
include_with_newtype_foreign_key
A nullable one-field newtype foreign key uses its leaf column rather than its presence-guarded embed expression when preloading a via relation.
include_with_unit_enum_foreign_key
A unit enum key and foreign key link through the enum’s discriminant column when preloading a via relation.
query_and_include_two_step_targets_and_values
Querying and including a two-step via returns distinct target models and scalar terminal values, grouped by parent for includes.
query_chain_scalar_via
A scalar-terminal via can also be navigated off a query (not just a loaded instance): User::filter(…).article_titles() yields the distinct titles reachable from the matched users.
query_scalar_via_two_step
A 2-step scalar via (comments.body): the terminal field sits directly on the first relation’s target, so the relation chain is a single step ([comments]) — the minimal scalar-via walk, distinct from the 3-step comments.article.title. Distinct values still apply, so a body repeated across comments appears once. Navigation and .include() must agree.
query_with_no_intermediates_is_empty
A user with no comments reaches no articles — an empty result, no error.
scalar_via_distinct_values_across_distinct_targets
Pins the distinct values decision against the alternative (distinct targets). The case that tells them apart is a user commenting on two different articles that happen to share a title: the model-via reaches two distinct targets, but the scalar via collapses their equal terminal values to one — ["Rust"], not ["Rust", "Rust"].
scalar_via_of_via
A via-of-via with a scalar terminal: nested_todo_titles routes through organizations.todos — where Organization::todos is itself a via — then projects Todo::title. This drives the nested-via splice (flatten_via_steps / RewriteVia) on the scalar-terminal code paths, which the model via-of-via (include_via_nested_via) leaves untested. Distinct values still apply, so a title shared by todos in different orgs collapses to one. Navigation and .include() must agree.
select_scalar_via
.select() of a scalar-terminal via returns the projected titles per parent row.
select_via_has_one
.select() of a single (has_one) via relation. Like include_via_has_one this drives the query.single via path, but through .select(), which projects each parent straight to its target rather than into a record slot. The missing-row path is already covered by the include test, so this focuses on a matched chain returning the target.
select_via_two_step
.select() of a multi-step via relation. .select() and .include() share the via-JOIN child query (build_relation_subquery); the difference is that .select() uses the subquery as the whole projection (each parent row decodes to its own Vec<Article>) rather than splicing it into a record slot. Distinct targets still apply, so Rust appears once though commented twice.
via_relation_query_can_be_filtered
A via relation query can be further filtered, like any other relation query.