feat(datasource-active-record): expose has_one :through as a to-one relation#327
Merged
Conversation
…elation (#325) * feat(datasource-active-record): expose has_one :through as a to-one relation Map a `has_one :through` association to a OneToOne (HasOne) schema instead of ManyToMany, so it renders as a to-one relation (matching forest_liana v1) rather than a to-many list. ActiveRecord resolves the join natively via the association name, so read, filter and aggregate all work through the relation. Resolve it efficiently too: when every hop of the through chain is a belongs_to (no scope/default_scope, same database), fold it into the existing to-one LEFT JOIN instead of issuing per-hop preload queries. When it does fall back to preload (e.g. the same relation is also used by a filter), the intermediate foreign key is now selected so ActiveRecord can still load it. * fix(datasource-active-record): address review feedback on has_one :through to-one - select the intermediate through FK only when it lives on the root table (the through hop is a belongs_to); a has_one through hop keeps the FK on the child, so selecting it against the root table emitted an invalid column. - count the intermediate through table in the JOIN safeguard so a separately projected relation on that same table falls back to preload instead of being JOINed twice (which ActiveRecord would alias, breaking column references). - document that a has_one :through to-one is display-only: it has no direct foreign key, so updating/associating it via the OneToOne write path is unsupported (the link lives on the intermediate table). - simplify belongs_to_chain_through?. * docs(datasource-active-record): drop comments added for has_one :through work * test(datasource-active-record): guard single JOIN for filter-joined has_one :through --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 new issues
|
| origin_key: association.klass.primary_key, | ||
| origin_key_target: @model.primary_key | ||
| ) | ||
| ) |
| origin_type_value: is_polymorphic ? @model.name : nil, | ||
| foreign_type_field: source_polymorphic ? association.source_reflection.foreign_type : nil, | ||
| foreign_type_value: source_polymorphic ? association.options[:source_type] : nil | ||
| ) |
| return if used_tables.include?(target.model.table_name) # a table joined twice would be aliased by AR | ||
| return if through_tables(collection, relation_name).intersect?(used_tables) | ||
|
|
||
| target |
| through = reflection.through_reflection | ||
| source = reflection.source_reflection | ||
|
|
||
| through && source && through.belongs_to? && source.belongs_to? && |
…ugh key root_through_foreign_key returns an array for a composite-key belongs_to through hop; interpolating it directly produced malformed SQL. Emit one select entry per key instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
matthv
approved these changes
Jul 3, 2026
forest-bot
added a commit
that referenced
this pull request
Jul 3, 2026
# [1.35.0](v1.34.2...v1.35.0) (2026-07-03) ### Features * **datasource-active-record:** expose has_one :through as a to-one relation ([#327](#327)) ([9788b60](9788b60))
Member
|
🎉 This PR is included in version 1.35.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
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.
Re-opens the work from #325 (has_one :through exposed as a to-one relation) with a corrected commit message: the footer is removed so semantic-release publishes this as a minor (1.35.0) rather than a major.
What it does
Maps a
has_one :throughassociation to a OneToOne (HasOne) schema instead of ManyToMany, so it renders as a to-one relation (matching forest_liana v1). ActiveRecord resolves the join natively via the association name, so read, filter and aggregate all work through the relation. When every hop of the through chain is a belongs_to (no scope, same database), it folds into the existing to-one LEFT JOIN instead of per-hop preload queries; otherwise it falls back to preload with the intermediate FK selected.Note
Expose non-polymorphic
has_one :throughassociations as OneToOne relations in ActiveRecord datasourcehas_one :throughassociations now produce aOneToOneSchemainstead ofManyToManySchemain the collection schema builder, withorigin_keyset to the target model's primary key.belongs_tohops,Utils::Querycollapses it into a single SQL query using LEFT OUTER JOINs; other chains fall back to preload.belongs_to_chain_through?,through_tables, androot_through_foreign_keyguard against unsafe JOINs and ensure intermediate foreign key columns are selected.has_one :through(non-polymorphic) relation that previously appeared as ManyToMany in Forest Admin will now appear as OneToOne, which may affect how it is queried or displayed.Macroscope summarized 0ee19cd.