From cd994eff9a343df376bfaec59de5b24a2ab51256 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 28 Jun 2008 01:27:25 +0100 Subject: Allow conditions on multiple tables to be specified using hash. Examples: User.all :joins => :items, :conditions => { :age => 10, :items => { :color => 'black' } } Item.first :conditions => { :items => { :color => 'red' } } Note : Hash key in :conditions is referring to the actual table name or the alias defined in query. --- activerecord/CHANGELOG | 5 +++++ activerecord/lib/active_record/base.rb | 24 +++++++++++++++--------- activerecord/test/cases/finder_test.rb | 17 +++++++++++++++++ 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 4e05d0fcfd..d6cc589381 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,10 @@ *Edge* +* Allow conditions on multiple tables to be specified using hash. [Pratik Naik]. Example: + + User.all :joins => :items, :conditions => { :age => 10, :items => { :color => 'black' } } + Item.first :conditions => { :items => { :color => 'red' } } + * Always treat integer :limit as byte length. #420 [Tarmo Tänav] * Partial updates don't update lock_version if nothing changed. #426 [Daniel Morrison] diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 021aaf4ca1..962c2b36d9 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1999,24 +1999,28 @@ module ActiveRecord #:nodoc: # # => "age BETWEEN 13 AND 18" # { 'other_records.id' => 7 } # # => "`other_records`.`id` = 7" + # { :other_records => { :id => 7 } } + # # => "`other_records`.`id` = 7" # And for value objects on a composed_of relationship: # { :address => Address.new("123 abc st.", "chicago") } # # => "address_street='123 abc st.' and address_city='chicago'" - def sanitize_sql_hash_for_conditions(attrs) + def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name) attrs = expand_hash_conditions_for_aggregates(attrs) conditions = attrs.map do |attr, value| - attr = attr.to_s + unless value.is_a?(Hash) + attr = attr.to_s + + # Extract table name from qualified attribute names. + if attr.include?('.') + table_name, attr = attr.split('.', 2) + table_name = connection.quote_table_name(table_name) + end - # Extract table name from qualified attribute names. - if attr.include?('.') - table_name, attr = attr.split('.', 2) - table_name = connection.quote_table_name(table_name) + "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" else - table_name = quoted_table_name + sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s)) end - - "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" end.join(' AND ') replace_bind_variables(conditions, expand_range_bind_variables(attrs.values)) @@ -2070,6 +2074,8 @@ module ActiveRecord #:nodoc: expanded = [] bind_vars.each do |var| + next if var.is_a?(Hash) + if var.is_a?(Range) expanded << var.first expanded << var.last diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index f48b62ba6b..b97db73b68 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -200,6 +200,23 @@ class FinderTest < ActiveRecord::TestCase assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) } end + def test_find_on_hash_conditions_with_hashed_table_name + assert Topic.find(1, :conditions => {:topics => { :approved => false }}) + assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => {:topics => { :approved => true }}) } + end + + def test_find_with_hash_conditions_on_joined_table + firms = Firm.all :joins => :account, :conditions => {:accounts => { :credit_limit => 50 }} + assert_equal 1, firms.size + assert_equal companies(:first_firm), firms.first + end + + def test_find_with_hash_conditions_on_joined_table_and_with_range + firms = DependentFirm.all :joins => :account, :conditions => {:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }} + assert_equal 1, firms.size + assert_equal companies(:rails_core), firms.first + end + def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate david = customers(:david) assert Customer.find(david.id, :conditions => { 'customers.name' => david.name, :address => david.address }) -- cgit v1.2.3