0

我有一个Family包含 amother_id和 a的课程father_id。从Family 模型的角度来看,重要的是要知道哪个父母是母亲,哪个是父亲,但是母亲和父亲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 似乎对 double 很满意belongs_to。但是,my_dad.family == nil表示第二个has_one覆盖第一个。这是合理的,因为否则,如果 resident_id 同时出现在 mother_id 和父亲 ID 列中会发生什么?(虽然我计划添加模型级验证以确保永远不会发生,has_one但不涉及验证方法。)此外,这my_dad.family = Family.new意味着什么?ActiveRecord 将如何选择是否my_dad.id插入Family.mother_idFamily.father_id

这个 Stackoverflow question,我得到了使用不同名称的想法,即将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) 数据库级验证是否可以补充模型级验证以确保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),因为没有设置器,所以不会产生任何歧义。

4

1 回答 1

0

您面临的主要问题是您有一个“条件”外键,这意味着用于解析 :family 居民的外键取决于居民是男性还是女性(母亲或父亲)。在我看来,处理这个问题的最好方法是使用 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 表,但您需要迁移字符串类型的 :type 字段并根据具体情况存储值“Mother”或“Father”。此外,将这些类定义中的每一个都放在 models/.js 中自己的文件中。

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

编辑2:

给定当前架构,您需要在表上创建检查约束families。一方面,活动记录对此没有直接支持,因此您必须执行原始 sql 来添加约束。理论上,每次在“families”的“mother_id”列中添加或更改一个值时,检查都必须与“residents”表进行交叉引用,确定“resident”的“type”列是“母亲。” 将(理论上)添加此约束的 SQL 是

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

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

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

于 2012-09-23T04:06:51.963 回答