Как лучше всего связать адрес с несколькими моделями в рельсах?
Этот вопрос на SO, похоже, связан с моим вопросом, но я не уверен, что на мой вопрос ответили.
Адрес может принадлежать более чем одной модели (UserProfile и Event)
Какой правильный способ реализовать это?
Основные таблицы:
user_profiles(id)
events(id)
Параметры для реализации таблицы адресов:
-
addresses(id,user_profile_id,event_id)
Этот подход кажется клонистым, поскольку, если завтра адрес должен принадлежать еще одной модели, я должен добавить это поле id.
Кроме того, я еще не знаю, но добавление нового поля id может вызвать некоторый код
сломать также?
-
addresses(id,model_type,model_id)
Это полиморфно, правильно. Я не знаю почему, но я так опасаюсь этого?
-
Какой-нибудь другой способ сделать это?
Примечание:
Я мог бы сделать таблицы такими, я полагаю:
user_profiles(id,address_id)
events(id,address_id)
Но это означает, что тот же address_id
может принадлежать к разным моделям.
Я полагаю, что это не должно быть так, потому что, например, скажем, что адрес для события нужно изменить, но он не должен влиять на адрес user_profile
.
Так что это будет что-то вроде этого (что, я думаю, неверно):
@current_user_profile.address = some_new_address
#this would have changed the address for both the user_profile *and* the event
@current_user_profile.save
Ответы
Ответ 1
Один из способов - стандартный полиморфизм Rails:
class Address
belongs_to :addressable, :polymorphic => true
end
class UserProfile
has_one address, :as => :addressable
end
class Event
has_one address, :as => :addressable
end
Это может быть ошеломляющее чувство, которое у вас есть об этом, заключается в том, что вы не можете создать ограничение уровня db с полиморфными отношениями в стиле Rails. Альтернатива (предложенная Дэн Чак в Enterprise Rails) похожа на ваш вариант # 1, где вы действительно создаете отдельное поле id для каждого типа отношения. Это оставляет неиспользуемые поля, но также допускает ограничения. Я могу видеть аргумент для обоих, но сообщество Rails некоторое время использует AR-полиморфизм с очевидным успехом. Я использую его без колебаний. Но если это вас задевает, вы можете использовать подход Чак. Это намного больше работы.:)
EDIT: @Slick86, миграция будет выглядеть примерно так:
class CreateAddresses < ActiveRecord::Migration
def change
create_table :addresses do |t|
t.integer :addressable_id
t.string :addressable_type
end
end
end
Ответ 2
Вы пропустили один из вариантов: введите класс, который содержит общее поведение, и добавьте поля во все таблицы. Для управления данными используйте скомпилированный агрегат.
class Address
attr_accessor :line1, :line2, :city, :state, :zip
def initialize(line1, line2, city, state, zip)
@line1 = line1
end
end
class UserProfile < ActiveRecord::Base
composed_of :address, :mapping => %w(line1 line2 city state zip)
end
class Event < ActiveRecord::Base
composed_of :address, :mapping => %w(line1 line2 city state zip)
end
См. # created_of в документах API Ruby on Rails.