2012-09-23 3 views
0

У меня есть класс Family, который включает в себя mother_id и father_id. С точки зрения модели семьи важно знать, какой родитель является матерью, а кто отец, но мать и отец Residents имеют все те же атрибуты (то есть столбцы базы данных). Поэтому в идеале я бы хотел, чтобы мои файлы модели выглядели следующим образом:has_one с более чем одной возможной колонкой с внешним ключом

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 переопределяет первое. Это разумно, потому что в противном случае, что произойдет, если resident_id появился в обеих столбцах mother_id и father_id? (Хотя я планирую добавить проверку уровня модели, чтобы гарантировать, что это никогда не произойдет, has_one не говорит о методах валидации.) Кроме того, что означает my_dad.family = Family.new? Как бы ActiveRecord выбрать, следует ли вставить my_dad.id в Family.mother_id или Family.father_id?

От this 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_id и father_id столбцов?

3) Можете ли вы назвать лучшие имена, чем husband_and_kids/wife_and_kids? (Правда, не программирования вопрос ...)

EDIT: Мне пришло в голову, чтобы добавить семейный добытчик:

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), не создавая никакой двусмысленности, так как нет сеттера.

ответ

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 таблицу, но вам нужно перенести: тип поля типа строки и сохранить значение «Мать» или «отец» в зависимости от случая. Кроме того, поместите каждое из этих определений классов в свой собственный файл в models /.

Редактировать: Я думаю, что это также решает проблемы, предложенные в ваших вторых и третьих вопросах.

Edit2:

Учитывая текущую схему, вам нужно будет создать check constraint на вашем families столе.Для одного, активная запись не имеет прямой поддержки для этого, поэтому вам нужно будет выполнить raw 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 для специфики).

Если вы действительно хотите реализовать валидацию на уровне базы данных здесь, вам, вероятно, потребуется изменить схему, разделив «жителей» на «матери» и «отцы».

+0

Я не понимаю, почему он отвечает на мой второй вопрос. Если у резидента есть тип «мужчина», но я пытаюсь вставить его id в Family.mother_id, база данных не будет знать, что это неверно. –

+0

Я полагаю, что это косвенно отвечает на второй вопрос, потому что он делает явные проверки ненужными. При создании новой записи, если вы ограничиваете себя созданием экземпляров только объектов Mother или объектов Father, нет никакой двусмысленности в отношении внешнего ключа. Объект Mother имеет внешний ключ: mother_id в семейной таблице, и это невозможно путать с: father_id – cdesrosiers

+0

Но эта проверка выполняется в модели, то есть в приложении. Вопрос № 2 касается проверки уровня базы данных - т. Е. что, если в будущем другому нужно подключиться к базе данных - как я могу обеспечить, чтобы разработчики будущего приложения не смогли совершить ошибку, поставив отца в столбце mother_id? Когда вы rawn 'rails генерируете семейство моделей mother_id: integer: uniq', rails помещает в файл схемы: ' add_index "family", ["mother_id"],: name => "index_families_on_mother_id",: unique => true' Мне нужна аналогичная проверка, в которой говорится, что столбца mother_id не может быть введено в столбце father_id –

Смежные вопросы