rmcsharry
rmcsharry

Reputation: 5552

Rails 4 - has_one and belongs_to throwing errors

I have read this, so I do understand the difference.

But I have inherited an app that is throwing strange behaviour (I think, perhaps I am wrong and this is normal).

There are 2 models:

class Pod < ActiveRecord::Base  
  has_one :pod_admin
end

class PodAdmin < ActiveRecord::Base  
  belongs_to :pod
end

In the rails console, I tried this:

p = Pod.find(5)

and it shows this Pod has a pod_admin_id value of 14. This is correct.

I tried to change the PodAdmin:

p.pod_admin = PodAdmin.last

and it throws this error:

NoMethodError: undefined method pod_admin_id for #<PodAdmin:0x007fa401f1e710>

Why is that? What am I missing?

EDIT

Based on comments/answers, without changing the models, I tried this:

pa = PodAdmin.last
pa.pod = p

and that works, I see the console return the last PodAdmin with a new pod_id.

BUT

pa.save

AND

p.save

both throw the same error as before.

If I look at the database schema, the Pod table has a pod_admin_id field and the PodAdmin table has a pod_id field.

I inherited this schema and I am just wondering if the original developer set this up correctly. Surely I should be able to update the relationship from either direction - isn't that the point of creating has_one and belongs_to, so you can have bi-directional relationships like this?

EDIT 2

I found the problem, which is that I had added this line to PodAdmin table instead of the Pod table:

  validates :pod_admin_id, uniqueness: {scope: :id, message: 'The Pod already has a PodAdmin'}

Apologies - but as you can see, what I am trying to achieve here is to prevent a Pod from having 2 PodAdmins. This validates does not seem to achieve that.

I can do this:

p = Pod.find(5)
pa_last = PodAdmin.last
pa_first = PodAdmin.first
pa_last = p
pa_first = p
pa_last.save
pa_first.save

and now both pa's have the same pod_id. How can I prevent that from happening?

EDIT 3

After much reading and testing and with thanks to both @Anand and @Spickerman the problem was that the previous developer put a foreign key into both tables (the has_one and the belongs_to). Only the belongs_to table should have a foreign key. Also, the relationship had been defined the wrong way round. However, fixing this does not guarantee a robust solution. I highly recommend others with similar issues read this.

Upvotes: 1

Views: 245

Answers (2)

Anand
Anand

Reputation: 3760

Instead of p.pod_admin = PodAdmin.last, call PodAdmin.last.pod = p - as others have mentioned, the pod_id is in the PodAdmin table, and not the other way around.

Update:

Based on update to question, the problem is because you have foreign key references both ways - you should either have pod_id in pod_admins table or pod_admin_id in pods table, but not both. Remove one of them with a new migration, and try again

> bundle exec rails g migration RemovePodIdFromPodAdmins

# db/migrations/XXXX_remove_pod_admin_id_from_pods
def change
  remove_column :pods, :pod_admin_id
end

bundle exec rake db:migrate

Then, as suggested above, call

pa = PodAdmin.last
pa.pod = p
pa.save!

Upvotes: 1

spickermann
spickermann

Reputation: 106792

The foreign key always belongs to the Model with the belongs_to association.

In your example PodAdmin belongs_to Pod, therefore your pod_admins table should have a pod_id column.

Or you can just change your models to the following to reflect your database schema:

class Pod < ActiveRecord::Base  
  belongs_to :pod_admin
end

class PodAdmin < ActiveRecord::Base  
  has_one :pod
end

Upvotes: 1

Related Questions