has_one有多个可能的外键列

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

 class Resident  :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  '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似乎对double belongs_to很满意。 但是, my_dad.family == nil ,表示第二个has_one正在覆盖第一个。 这是合理的,因为否则,如果一个resident_id出现在mother_id和father_id列中,会发生什么? (虽然我计划添加模型级validation以确保永远不会发生,但是has_one不会与validation方法对话。)此外, my_dad.family = Family.new是什么意思? ActiveRecord如何选择是否将my_dad.id插入Family.mother_idFamily.father_id

从这个Stackoverflow问题 ,我有了使用不同名称的想法,即将has_one行更改为:

 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 

我的问题是:

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

2)数据库级validation是否可以补充模型级validation,以确保my_dad.id不能同时出现在mother_idfather_id列中?

3)你能想到比husband_and_kids / 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 ),没有造成任何歧义,因为没有制定者。

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

 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”。 另外,将每个类定义放在models /中自己的文件中。

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

EDIT2:

在给定当前架构的情况下,您需要在families表上创建一个检查约束 。 首先,活动记录没有直接支持,所以你必须执行原始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包含一个子查询,据我所知,许多数据库都不允许检查中的子查询。 (具体见这个问题 )。

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