rmcsharry rmcsharry - 6 months ago 13
Ruby Question

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.

Answer

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!
Comments