Rails 4 - has_one 和 belongs_to 抛出错误

Rails 4 - has_one and belongs_to throwing errors

我读过this,所以我明白其中的区别。

但我继承了一个引发奇怪行为的应用程序(我想,也许我错了,这是正常的)。

有 2 个模型:

class Pod < ActiveRecord::Base  
  has_one :pod_admin
end

class PodAdmin < ActiveRecord::Base  
  belongs_to :pod
end

在 rails 控制台中,我尝试了这个:

p = Pod.find(5)

它显示此 Pod 的 pod_admin_id 值为 14。这是正确的。

我尝试更改 PodAdmin:

p.pod_admin = PodAdmin.last

它抛出这个错误:

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

这是为什么?我错过了什么?

编辑

基于comments/answers,在不改变模型的情况下,我尝试了这个:

pa = PodAdmin.last
pa.pod = p

这很有效,我看到控制台 return 最后一个带有新 pod_id 的 PodAdmin。

但是

pa.save

p.save

都抛出与之前相同的错误。

如果我查看数据库架构,Pod table 有一个 pod_admin_id 字段,PodAdmin table 有一个 pod_id 字段。

我继承了这个架构,我只是想知道原始开发人员是否正确设置了它。当然,我应该能够从任一方向更新关系 - 这不是创建 has_one 和 belongs_to 的重点,所以你可以拥有这样的双向关系吗?

编辑 2

我发现了问题,这是我将此行添加到 PodAdmin table 而不是 Pod table:

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

抱歉 - 但如您所见,我在这里试图实现的是防止一个 Pod 拥有 2 个 PodAdmin。这个验证似乎没有实现。

我能做到:

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

现在两个 pa 都具有相同的 pod_id。 我怎样才能防止这种情况发生?

编辑 3

经过大量阅读和测试并感谢@Anand 和@Spickerman,问题是之前的开发人员将外键放入两个 table 中(has_onebelongs_to).只有 belongs_to table 应该有外键。此外,这种关系被错误地定义了。然而,解决这个问题并不能保证一个可靠的解决方案。我强烈建议其他有类似问题的人阅读 this

外键始终属于具有 belongs_to 关联的模型。

在您的示例中 PodAdmin belongs_to Pod,因此您的 pod_admins table 应该有一个 pod_id 列。

或者您可以将模型更改为以下内容以反映您的数据库架构:

class Pod < ActiveRecord::Base  
  belongs_to :pod_admin
end

class PodAdmin < ActiveRecord::Base  
  has_one :pod
end

调用 PodAdmin.last.pod = p 而不是 p.pod_admin = PodAdmin.last - 正如其他人提到的,pod_id 在 PodAdmin table 中,而不是相反。

更新:

根据对问题的更新,问题是因为你有两种方式的外键引用 - 你应该在 pod_admins table 或 pod_admin_id 中有 pod_id pods table,但不是两者。使用新迁移删除其中一个,然后重试

> 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

然后,按照上面的建议,调用

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