限制Rails中的has_one关系

时间:2016-01-04 22:52:28

标签: ruby-on-rails ruby activerecord

我有两个像这样的ActiveRecord模型:

class User < ActiveRecord::Base
  has_one :contact_information
end

class ContactInformation < ActiveRecord::Base
  belongs_to :user
end

这是我在用户和联系信息表之间建立一对一关系的设置。

我遇到的问题是当我创建一个新的contact_information条目,该条目指向已经存在contact_information的用户时,会创建包含该关系的新记录,这意味着我有两个指向同一用户的contact_information记录,甚至虽然这是一对一的关系。

Rails似乎选择了第一条记录并将其返回。

例如在irb:

> User.create
> ContactInformation.create(user: User.last)
> ContactInformation.create(user: User.last)
> ContactInformation.all
=> #<ActiveRecord::Relation [#<ContactInformation id: 3, created_at: "2016-01-04 22:28:11", updated_at: "2016-01-04 22:28:11", user_id: 2>, #<ContactInformation id: 4, created_at: "2016-01-04 22:28:18", updated_at: "2016-01-04 22:28:18", user_id: 2>]>

将user_id设置为2。

理想的情况是,当为已经拥有contact_information的用户创建记录时,contact_information的验证失败。

我想出了这种用于创建的联系信息模型的初始自定义验证方法(需要进行更新)

validate :check_parent, on: :create

def check_parent
    if user.contact_information.id.nil?
        errors.add(:user, "already has contact information")
    end
end

我想知道是否有更多的方法可以解决这个问题?或者我是否需要为所有has_one关系创建这样的自定义验证器?

由于

2 个答案:

答案 0 :(得分:1)

您需要进行验证,但不需要自定义验证:

class ContactInformation < ActiveRecord::Base
  belongs_to :user
  validates_uniqueness_of :user_id
end

您可能还希望在该列的数据库中添加唯一索引。它不仅可以提高查询效果,还可以保护您的应用免受race conditions in case you are running multiple instances of it in parallel

答案 1 :(得分:1)

您可以选择几种方式。

您可以在ContactInformation型号上使用validates_uniqueness_of

class ContactInformation < ActiveRecord::Base
  validates_uniqueness_of :user_id
end

然而,这将查询数据库以确保每次保存都不存在重复项,并且可能非常慢。

另一个选择是让您的数据库为您处理此问题。根据您使用ActiveRecord的内容,在user_id表的contact_informations列上添加唯一性约束可能很有价值。

如果你的数据集很大,我建议不要使用validates_uniqueness_of,否则它可能是完美的。

确保至少在该表的user_id列中添加索引:

class AddIndexOnContactInformationToUserId < ActiveRecord::Migration
  add_index :contact_information, :user_id
end