Add where.associated to check association presence

This commit is contained in:
Kasper Timm Hansen 2020-11-26 14:15:18 +01:00
parent dda5e28a60
commit ccdf6b8d42
No known key found for this signature in database
GPG Key ID: 191153215EDA53D8
3 changed files with 58 additions and 0 deletions

View File

@ -1,3 +1,17 @@
* Add `where.associated` to check for the presence of an association.
```ruby
# Before:
account.users.joins(:contact).where.not(contact_id: nil)
# After:
account.users.where.associated(:contact)
```
Also mirrors `where.missing`.
*Kasper Timm Hansen*
* Fix odd behavior of inverse_of with multiple belongs_to to same class.
Fixes #35204.

View File

@ -48,6 +48,34 @@ module ActiveRecord
@scope
end
# Returns a new relation with joins and where clause to identify
# associated relations.
#
# For example, posts that are associated to a related author:
#
# Post.where.associated(:author)
# # SELECT "posts".* FROM "posts"
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# # WHERE "authors"."id" IS NOT NULL
#
# Additionally, multiple relations can be combined. This will return posts
# associated to both an author and any comments:
#
# Post.where.associated(:author, :comments)
# # SELECT "posts".* FROM "posts"
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
# # WHERE "authors"."id" IS NOT NULL AND "comments"."id" IS NOT NULL
def associated(*associations)
associations.each do |association|
reflection = @scope.klass._reflect_on_association(association)
@scope.joins!(association)
self.not(reflection.table_name => { reflection.association_primary_key => nil })
end
@scope
end
# Returns a new relation with left outer joins and where clause to identify
# missing relations.
#

View File

@ -12,6 +12,22 @@ module ActiveRecord
class WhereChainTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :humans, :essays
def test_associated_with_association
Post.where.associated(:author).tap do |relation|
assert_includes relation, posts(:welcome)
assert_includes relation, posts(:sti_habtm)
assert_not_includes relation, posts(:authorless)
end
end
def test_associated_with_multiple_associations
Post.where.associated(:author, :comments).tap do |relation|
assert_includes relation, posts(:welcome)
assert_not_includes relation, posts(:sti_habtm)
assert_not_includes relation, posts(:authorless)
end
end
def test_missing_with_association
assert posts(:authorless).author.blank?
assert_equal [posts(:authorless)], Post.where.missing(:author).to_a