has_one有多个可能的外键列

时间:2012-09-23 03:30:25

标签: ruby-on-rails activerecord

我有一个Family课程,其中包含mother_idfather_id。从家庭模型的角度来看,重要的是要知道哪个父母是母亲,哪个是父亲,但母亲和父亲Residents具有所有相同的属性(即数据库列)。理想情况下,我希望我的模型文件看起来像这样:

class Resident < ActiveRecord::Base
  has_one :family, :dependent => :nullify, :foreign_key => :father_id
  has_one :family, :dependent => :nullify, :foreign_key => :mother_id
  attr_accessible :email, :cell, :first_name, :last_name
end

class Family < ActiveRecord::Base
  belongs_to :father, :class_name => 'Resident', :foreign_key => 'father_id'
  belongs_to :mother, :class_name => 'Resident', :foreign_key => 'mother_id'
  attr_accessible :address, :city, :state, :number_of_children
end

这不起作用。 my_family.mothermy_family.father工作,因此Rails似乎对双belongs_to感到满意。但是,my_dad.family == nil表示第二个has_one覆盖了第一个has_one。这是合理的,因为否则,如果一个resident_id出现在mother_id和father_id列中会发生什么? (虽然我计划添加模型级验证以确保永远不会发生,但my_dad.family = Family.new不会与验证方法对话。)此外,my_dad.id意味着什么? ActiveRecord如何选择是将Family.mother_id插入Family.father_id还是has_one

this Stackoverflow question开始,我想到了使用不同的名称,即将has_one :wife_and_kids, :class_name => 'Family', :dependent => :nullify, :foreign_key => :father_id has_one :husband_and_kids, :class_name => 'Family', :dependent => :nullify, :foreign_key => :mother_id 行更改为:

my_dad.id

我的问题是:

1)有更好的方法吗?可能是一个不同的数据库架构?

2)数据库级验证是否可以补充模型级验证,以确保mother_idfather_id列中都无法显示husband_and_kids

3)你能想到比wife_and_kids / def family @family ||= self.wife_and_kids || self.husband_and_kids end after_save :reset_family def reset_family @family = nil end 更好的名字吗? (不可否认,这不是编程问题......)

修改 在我看来添加一个家庭吸气剂:

[husband|wife]_and_kids

这使得语法更清晰(因为我真的不是{{1}}的粉丝),没有任何歧义,因为没有setter。

1 个答案:

答案 0 :(得分:0)

您面临的主要问题是您拥有“有条件”外键,这意味着用于解决居民家庭的外键取决于居民是男性还是女性(母亲或父亲)。在我看来,解决这个问题的最佳方法是使用STI(单表继承)来区分这两种情况。

class Resident < ActiveRecord::Base
    attr_accessible :email, :cell, :first_name, :last_name
end

class Mother < Resident
    has_one :family, :dependent => :nullify, :foreign_key => :mother_id
end

class Father < Resident
    has_one :family, :dependent => :nullify, :foreign_key => :father_id
end

您仍然可以使用Resident表,但是您需要迁移类型为string的:type字段并根据具体情况存储值“Mother”或“Father”。另外,将每个类定义放在模型/.

中的自己的文件中

编辑:我认为这也解决了第二和第三个问题中提出的问题。

EDIT2:

根据当前架构,您需要在families表上创建check constraint。首先,活动记录没有直接支持,所以你必须执行原始sql来添加约束。理论上,每次在“家庭”的“mother_id”栏中添加或更改值时,检查必须与“居民”表交叉引用,确定“居民”的“类型”列是“母亲。” (理论上)添加此约束的SQL是

ALTER TABLE families
ADD CONSTRAINT must_be_mother CHECK ((SELECT type FROM residents WHERE residents.id = families.mother_id) = 'Mother')

问题是这个CHECK包含一个子查询,据我所知,许多数据库都不允许检查中的子查询。 (有关详细信息,请参阅此question。)

如果您真的想在此处实施数据库级验证,则可能需要通过将“居民”分为“母亲”和“父亲”来更改架构。