我有一个Family
课程,其中包含mother_id
和father_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.mother
和my_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_id
和father_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。
答案 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。)
如果您真的想在此处实施数据库级验证,则可能需要通过将“居民”分为“母亲”和“父亲”来更改架构。