mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Better support for where()
conditions that use an association name.
Using the name of an association in `where` previously worked only if the value was a single `ActiveRecrd::Base` object. e.g. Post.where(author: Author.first) Any other values, including `nil`, would cause invalid SQL to be generated. This change supports arguments in the `where` query conditions where the key is a `belongs_to` association name and the value is `nil`, an `Array` of `ActiveRecord::Base` objects, or an `ActiveRecord::Relation` object. # Given the Post model class Post < ActiveRecord::Base belongs_to :author end # nil value finds records where the association is not set Post.where(author: nil) # SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IS NULL # Array values find records where the association foreign key # matches the ids of the passed ActiveRecord models, resulting # in the same query as Post.where(author_id: [1,2]) authors_array = [Author.find(1), Author.find(2)] Post.where(author: authors_array) # ActiveRecord::Relation values find records using the same # query as Post.where(author_id: Author.where(last_name: "Emde")) Post.where(author: Author.where(last_name: "Emde")) Polymorphic `belongs_to` associations will continue to be handled appropriately, with the polymorphic `association_type` field added to the query to match the base class of the value. This feature previously only worked when the value was a single `ActveRecord::Base`. class Post < ActiveRecord::Base belongs_to :author, polymorphic: true end Post.where(author: Author.where(last_name: "Emde")) # Generates a query similar to: Post.where(author_id: Author.where(last_name: "Emde"), author_type: "Author")
This commit is contained in:
parent
0dea33f770
commit
8062a30794
3 changed files with 104 additions and 3 deletions
|
@ -1,3 +1,58 @@
|
||||||
|
* Better support for `where()` conditions that use a `belongs_to`
|
||||||
|
association name.
|
||||||
|
|
||||||
|
Using the name of an association in `where` previously worked only
|
||||||
|
if the value was a single `ActiveRecord::Base` object. e.g.
|
||||||
|
|
||||||
|
Post.where(author: Author.first)
|
||||||
|
|
||||||
|
Any other values, including `nil`, would cause invalid SQL to be
|
||||||
|
generated. This change supports arguments in the `where` query
|
||||||
|
conditions where the key is a `belongs_to` association name and the
|
||||||
|
value is `nil`, an `Array` of `ActiveRecord::Base` objects, or an
|
||||||
|
`ActiveRecord::Relation` object.
|
||||||
|
|
||||||
|
class Post < ActiveRecord::Base
|
||||||
|
belongs_to :author
|
||||||
|
end
|
||||||
|
|
||||||
|
`nil` value finds records where the association is not set:
|
||||||
|
|
||||||
|
Post.where(author: nil)
|
||||||
|
# SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IS NULL
|
||||||
|
|
||||||
|
`Array` values find records where the association foreign key
|
||||||
|
matches the ids of the passed ActiveRecord models, resulting
|
||||||
|
in the same query as `Post.where(author_id: [1,2])`:
|
||||||
|
|
||||||
|
authors_array = [Author.find(1), Author.find(2)]
|
||||||
|
Post.where(author: authors_array)
|
||||||
|
# SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IN (1, 2)
|
||||||
|
|
||||||
|
`ActiveRecord::Relation` values find records using the same
|
||||||
|
query as `Post.where(author_id: Author.where(last_name: "Emde"))`
|
||||||
|
|
||||||
|
Post.where(author: Author.where(last_name: "Emde"))
|
||||||
|
# SELECT "posts".* FROM "posts"
|
||||||
|
# WHERE "posts"."author_id" IN (
|
||||||
|
# SELECT "authors"."id" FROM "authors"
|
||||||
|
# WHERE "authors"."last_name" = 'Emde')
|
||||||
|
|
||||||
|
Polymorphic `belongs_to` associations will continue to be handled
|
||||||
|
appropriately, with the polymorphic `association_type` field added
|
||||||
|
to the query to match the base class of the value. This feature
|
||||||
|
previously only worked when the value was a single `ActveRecord::Base`.
|
||||||
|
|
||||||
|
class Post < ActiveRecord::Base
|
||||||
|
belongs_to :author, polymorphic: true
|
||||||
|
end
|
||||||
|
|
||||||
|
Post.where(author: Author.where(last_name: "Emde"))
|
||||||
|
# Generates a query similar to:
|
||||||
|
Post.where(author_id: Author.where(last_name: "Emde"), author_type: "Author")
|
||||||
|
|
||||||
|
*Martin Emde*
|
||||||
|
|
||||||
* Respect temporary option when dropping tables with MySQL.
|
* Respect temporary option when dropping tables with MySQL.
|
||||||
|
|
||||||
Normal DROP TABLE also works, but commits the transaction.
|
Normal DROP TABLE also works, but commits the transaction.
|
||||||
|
|
|
@ -55,9 +55,9 @@ module ActiveRecord
|
||||||
#
|
#
|
||||||
# For polymorphic relationships, find the foreign key and type:
|
# For polymorphic relationships, find the foreign key and type:
|
||||||
# PriceEstimate.where(estimate_of: treasure)
|
# PriceEstimate.where(estimate_of: treasure)
|
||||||
if klass && value.is_a?(Base) && reflection = klass.reflect_on_association(column.to_sym)
|
if klass && reflection = klass.reflect_on_association(column.to_sym)
|
||||||
if reflection.polymorphic?
|
if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
|
||||||
queries << build(table[reflection.foreign_type], value.class.base_class)
|
queries << build(table[reflection.foreign_type], base_class)
|
||||||
end
|
end
|
||||||
|
|
||||||
column = reflection.foreign_key
|
column = reflection.foreign_key
|
||||||
|
@ -67,6 +67,18 @@ module ActiveRecord
|
||||||
queries
|
queries
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.polymorphic_base_class_from_value(value)
|
||||||
|
case value
|
||||||
|
when Relation
|
||||||
|
value.klass.base_class
|
||||||
|
when Array
|
||||||
|
val = value.compact.first
|
||||||
|
val.class.base_class if val.is_a?(Base)
|
||||||
|
when Base
|
||||||
|
value.class.base_class
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.references(attributes)
|
def self.references(attributes)
|
||||||
attributes.map do |key, value|
|
attributes.map do |key, value|
|
||||||
if value.is_a?(Hash)
|
if value.is_a?(Hash)
|
||||||
|
|
|
@ -35,6 +35,21 @@ module ActiveRecord
|
||||||
assert_equal Post.where(author_id: 1).to_sql, Post.where(author: author).to_sql
|
assert_equal Post.where(author_id: 1).to_sql, Post.where(author: author).to_sql
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_belongs_to_nil_where
|
||||||
|
assert_equal Post.where(author_id: nil).to_sql, Post.where(author: nil).to_sql
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_belongs_to_array_value_where
|
||||||
|
assert_equal Post.where(author_id: [1,2]).to_sql, Post.where(author: [1,2]).to_sql
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_belongs_to_nested_relation_where
|
||||||
|
expected = Post.where(author_id: Author.where(id: [1,2])).to_sql
|
||||||
|
actual = Post.where(author: Author.where(id: [1,2])).to_sql
|
||||||
|
|
||||||
|
assert_equal expected, actual
|
||||||
|
end
|
||||||
|
|
||||||
def test_belongs_to_nested_where
|
def test_belongs_to_nested_where
|
||||||
parent = Comment.new
|
parent = Comment.new
|
||||||
parent.id = 1
|
parent.id = 1
|
||||||
|
@ -55,6 +70,25 @@ module ActiveRecord
|
||||||
assert_equal expected.to_sql, actual.to_sql
|
assert_equal expected.to_sql, actual.to_sql
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_polymorphic_nested_array_where
|
||||||
|
treasure = Treasure.new
|
||||||
|
treasure.id = 1
|
||||||
|
hidden = HiddenTreasure.new
|
||||||
|
hidden.id = 2
|
||||||
|
|
||||||
|
expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: [treasure, hidden])
|
||||||
|
actual = PriceEstimate.where(estimate_of: [treasure, hidden])
|
||||||
|
|
||||||
|
assert_equal expected.to_sql, actual.to_sql
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_polymorphic_nested_relation_where
|
||||||
|
expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: Treasure.where(id: [1,2]))
|
||||||
|
actual = PriceEstimate.where(estimate_of: Treasure.where(id: [1,2]))
|
||||||
|
|
||||||
|
assert_equal expected.to_sql, actual.to_sql
|
||||||
|
end
|
||||||
|
|
||||||
def test_polymorphic_sti_shallow_where
|
def test_polymorphic_sti_shallow_where
|
||||||
treasure = HiddenTreasure.new
|
treasure = HiddenTreasure.new
|
||||||
treasure.id = 1
|
treasure.id = 1
|
||||||
|
|
Loading…
Reference in a new issue