Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Rails 3: find_or_create_by gives ArgumentError (Unknown keys) with hash parameter in has_many

#6741

This is the code that fails, you can put it in a main.rb file and run it:

require 'rubygems'
require 'active_record'

ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')

ActiveRecord::Schema.define do
  create_table "countries", :force => true do |t|
    t.string "name"
  end

  create_table "people", :force => true do |t|
    t.integer "country_id"
    t.string "name"
    t.integer "age"
  end
end

class Country < ActiveRecord::Base
  has_many :people
end

class Person < ActiveRecord::Base
  belongs_to :country
end

country = Country.create! :name => 'Foo'
country.people.find_or_create_by_name 'bar', :age => 30

I get

/Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activesupport-3.0.3/lib/active_support/core_ext/hash/keys.rb:43:in `assert_valid_keys': Unknown key(s): age (ArgumentError)
    from /Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activerecord-3.0.3/lib/active_record/relation/spawn_methods.rb:107:in `apply_finder_options'
    from /Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activerecord-3.0.3/lib/active_record/base.rb:934:in `construct_finder_arel'
    from /Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activerecord-3.0.3/lib/active_record/base.rb:986:in `method_missing'
    from /Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activerecord-3.0.3/lib/active_record/associations/association_collection.rb:443:in `block in method_missing'
    from /Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activerecord-3.0.3/lib/active_record/base.rb:1121:in `with_scope'
    from /Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activerecord-3.0.3/lib/active_record/associations/association_proxy.rb:203:in `with_scope'
    from /Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activerecord-3.0.3/lib/active_record/associations/association_collection.rb:439:in `method_missing'
    from /Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activerecord-3.0.3/lib/active_record/associations/association_proxy.rb:151:in `send'
    from /Users/asterite/.rvm/gems/ruby-1.9.2-p136@geochat-rails/gems/activerecord-3.0.3/lib/active_record/associations/association_collection.rb:425:in `method_missing'
    from main.rb:27:in `<main>'

Reported by Ary Borenszweig · April 25th, 2011 @ 06:08 AM

State: new
Milestone: none
Assigned to: Aaron Patterson Aaron Patterson
Importance: Low

Activity

  1. Ben Orenstein
    Ben Orenstein
    • Importance changed from to Low

    April 25th, 2011 @ 03:42 PM

  2. Ben Orenstein
    Ben Orenstein
    • State changed from new to open

    April 25th, 2011 @ 03:43 PM

  3. Ben Orenstein
    Ben Orenstein
    • State changed from open to new

    April 25th, 2011 @ 03:43 PM

  4. Ary Borenszweig
    Ary Borenszweig

    Why low priority? The beauty of Rails 3 is to work with associations like this, and I can see many use cases where you would want to find or create by an attribute and initialize it with other attributes. It's not an edge case, I already wanted to use this in two projects I started recently.

    April 25th, 2011 @ 03:53 PM

  5. Matthew Daubert
    Matthew Daubert

    Ary,

    I suspect the low priority designation is because you an accomplish the same task by changing the last line. I plugged this into your test case and it seems to work fine.

    country.people.find_or_create_by_name('bar') { |p| p.age = 30 }
    

    http://api.rubyonrails.org/classes/ActiveRecord/Base.html

    April 25th, 2011 @ 05:00 PM

  6. Flurin
    Flurin

    That last one doesn't work either, the block never get's called.

    April 28th, 2011 @ 10:00 AM

  7. Nick Howard
    Nick Howard

    I looked into this. What it looks like is happening is that ActiveRecord::Association::CollectionProxy's way of handling find_or_create_by calls is written in a manner that does not allow for setting additional attributes when the lookup fails. Any arguments to it are passed to a find_by_ call instead of being used to attributes for creating a new object if it didn't exist.

    I put together a patch for rails master.

    https://github.com/rails/rails/pull/358

    What do you think?

    May 1st, 2011 @ 11:34 PM

  8. Nick Howard
  9. Nick Howard
    Nick Howard
    • Assigned user set to Aaron Patterson

    May 6th, 2011 @ 10:31 PM