Belongs_to behaviour and missing method updated?

Hi,

I’ve got a really simple model and controller which are producing an
error. The model is one line:

class NaturalPerson < ActiveRecord::Base
  belongs_to :nationality, {:class_name => "Country"}
end

and the controller, specifically the create action, is equally simple:

class NaturalPeopleController < ApplicationController
  def create
    person = NaturalPerson.new(params[:natural_person])

    if person.save
      flash[:notice] = "Person was successfully created"
      redirect_to :action => "list"
    else
      render_scaffold('new')
    end
  end
end

If I run the code like this I get this error:

ActiveRecord::AssociationTypeMismatch in Natural

peopleController#create
Country expected, got String

I have given ActiveRecord enough information to know it should
instantiate a Country and assign to “nationality”, no?

OK, so I need to do it myself in a constructor for the NaturalPerson
model:

def initialize(params)
@postal_address = params[:postal_address]
@fax_number = params[:fax_number]
@name = params[:name]
@phone_number = params[:phone_number]
@dob = Date.civil(params[“dob(1i)”].to_i, params[“dob(2i)”].to_i,
params[“dob(3i)”].to_i)
@visa =params[:visa]
@mobile_number = params[:mobile_number]
@email_address = params[:email_address]
@nationality = Country.find(params[:nationality].to_i)
end

Now I get this error:

NoMethodError in Natural peopleController#create
undefined method `updated?' for #<Country:0x412cb0a0
  @attributes={"name"=>"TONGA", "id"=>"220"}>

A search of the web and usenet suggests that the “definitive” reason for
this error is described in this post:

http://lists.rubyonrails.org/pipermail/rails/2006-March/026246.html

"my problem was caused by a badly chosen name of a variable
 in the validates method of my interface model. Originally
 I called it @device, after renaming it the error was gone."

Unfortuately the post doesn’t say what it was about the variable name
what was bad. This thread provides another useful tidbit of information:

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/28afcde31492865f/11bbc512dd6215bf?lnk=st&q=undefined+method+`updated%3F'&rnum=1#11bbc512dd6215bf

"Looking back at the reason, it would never get into the block
 that I mentioned in my original post if the :belongs_to object
 is null and if i have a different variable name, that would be
 the case and the association is only saved due to the link
 from the Doctor's side."

OK. In my case the belongs_to object is nil because it hasn’t been
assigned a value yet.

The name of my foreign key was originally “nationality” but, without
really knowing what was causing the problem, I thought there might be
some conflict with the instance variable so I changed the field to
“country_id”. Hence this code in the model:

belongs_to :nationality, {:class_name => "Country"}

which implies a foreign key of “country_id”.

So I’m at a point where I don’t think the previous examples of this
error apply to me. If anyone has any suggestions I would be extremely
grateful!

Regards,

Michael

Michael H. wrote:

Hi,

I’ve got a really simple model and controller which are producing an
error. The model is one line:

Hello,

it would be helpful to know what params[:natural_person] looks like to
analyze the first of the errors you described (one without overriding
the constructor)


Agnieszka F.

Agnieszka F. wrote:

Michael H. wrote:

Hi,

I’ve got a really simple model and controller which are producing an
error. The model is one line:

Hello,

it would be helpful to know what params[:natural_person] looks like to
analyze the first of the errors you described (one without overriding
the constructor)

Sure, here it is with some formatting to make it more readable:

Parameters: {
“natural_person”=> {
“postal_address”=>"",
“fax_number”=>"",
“name”=>“Maikolo”,
“phone_number”=>"",
“dob(1i)”=>“2006”,
“visa”=>"",
“dob(2i)”=>“12”,
“dob(3i)”=>“14”,
“mobile_number”=>"",
“email_address”=>"",
“nationality”=>“220”
},
“commit”=>“Create”
}

The view that produced the drop-down for “Nationality” looks like this:

Nationality
<%= options_from_collection_for_select(@countries, :id, :name, @selected) %>

…where @countries and @selected are defined in the controller as:

@countries = Country.find(:all)
@selected = Country.find_by_name("TONGA").id

@selected has the value of 220 (I didn’t change the country before
submitting the form).

Regards,

Michael

Just thinking out loud here…

What about if you changed the parameter to nationality_id instead of
nationality. That way, when the constructor is creating the object
calls
the method nationality_id= which is expecting a integer instead of the
the
method its currently calling nationality= which is created from the
belongs_to declaration.

In either case, your workaround does the trick but this might be a
little
more efficient.

Adam

I’ve found a workaround as outlined below.

I’ve got a really simple model and controller which are producing an
error. The model is one line:

class NaturalPerson < ActiveRecord::Base
  belongs_to :nationality, {:class_name => "Country"}
end

I’ve made no changes to the model. The constructor I included in my
initial post has been discarded.

and the controller, specifically the create action, is equally simple:

class NaturalPeopleController < ApplicationController
  def create
    person = NaturalPerson.new(params[:natural_person])

    if person.save
      flash[:notice] = "Person was successfully created"
      redirect_to :action => "list"
    else
      render_scaffold('new')
    end
  end
end

I’ve inserted this line of code before I instantiate the NaturalPerson:

params[:natural_person][:nationality] = 

Country.find(params[:country].to_i)

Note that I’m referring to a new form element: “country”. I have changed
the view so that this is the name of the country drop-down.

I don’t suppose anybody’s reading this but it gives me a sense of
closure to put my workaround on the list :slight_smile:

Michael

Adam R. wrote:

What about if you changed the parameter to nationality_id instead of
nationality.

Do you mean the association? It originally referred to a foreign key
which was also called “nationality” but I changed the foreign key to
“country_id” so that: a) it conforms to the ActiveRecord convention, and
b) removes any possibility of conflict between the association and field
names. Unfortunately this change didn’t affect the error.

That way, when the constructor is creating the object calls
the method nationality_id= which is expecting a integer instead of the
the
method its currently calling nationality= which is created from the
belongs_to declaration.

The belongs_to invocation should cause the same code to be run
regardless of the name of the association. I’m happy to be corrected on
that point. Also, I think the “_id” naming convention only applies to
the database and as such should be kept in the persistence layer and not
make its way to the middle tier of my application.

In either case, your workaround does the trick but this might be a
little more efficient.

I’m in the early stages of developing this application so I’m not that
bothered by efficiency yet. My main problem with the workaround is that
the Country.find() method will have to be invoked every time I want to
create a NaturalPerson. ie, it violates the DRY principle.

Michael

I was referring to the parameter passed in from the view…

so then your html would look like this:


<%= options_from_collection_for_select(@countries, :id, :name,
@selected) %>

I believe that should work and not require querying the Nationality
database.

Adam