Это может ввести в заблуждение людей, плохо знакомых с Rails (как я недавно), потому что некоторые части ответа имеют место в ваших Миграциях, а некоторые в ваших Моделях. Кроме того, вы действительно хотите смоделировать две разные вещи:
Адрес принадлежит одному клиенту, и у каждого клиента есть много адресов. В вашем случае это будет 1 или 2 адреса, но я бы посоветовал вам рассмотреть возможность того, что клиент может иметь более одного адреса доставки. Например, у меня есть 3 отдельных адреса доставки на Amazon.com.
Отдельно мы хотим смоделировать тот факт, что у каждого клиента есть адрес выставления счета и адрес доставки, который вместо этого может быть по умолчанию адрес доставки, если вы разрешите более одного адреса доставки.
Вот как бы вы это сделали:
Миграции
class CreateCustomers < ActiveRecord::Migration
create_table :customers do |t|
def up
t.references :billing_address
t.references :shipping_address
end
end
end
Здесь вы указываете, что в этой таблице есть два столбца, которые будут называться: billing_address и: shipping_address и которые содержат ссылки на другую таблицу. Rails фактически создаст для вас столбцы с именами 'billing_address_id' и 'shipping_address_id'. В нашем случае они будут ссылаться на строки в таблице адресов, но мы указываем это в моделях, а не в миграциях.
class CreateAddresses < ActiveRecord::Migration
create_table :addresses do |t|
def up
t.references :customer
end
end
end
Здесь вы также создаете столбец, который ссылается на другую таблицу, но в конце вы опускаете «_id». Rails позаботится об этом за вас, потому что видит, что у вас есть таблица 'users', соответствующая имени столбца (он знает о множественности).
Причина, по которой мы добавили «_id» в миграцию клиентов, заключается в том, что у нас нет таблицы «billing_addresses» или «shipping_addresses», поэтому нам нужно вручную указать полное имя столбца.
Модель
class Customer < ActiveRecord::Base
belongs_to :billing_address, :class_name => 'Address'
belongs_to :shipping_address, :class_name => 'Address'
has_many :addresses
end
Здесь вы создаете свойство в модели Customer с именем: billing_address, затем указываете, что это свойство связано с классом Address. Rails, увидев «own_to», будет искать столбец в таблице клиентов с именем «billing_address_id», который мы определили выше, и использовать этот столбец для хранения внешнего ключа. Затем вы делаете то же самое для адреса доставки.
Это позволит вам получить доступ к вашему адресу выставления счета и адресу доставки, в обоих случаях модели адреса, через экземпляр модели клиента, например:
@customer.billing_address # Returns an instance of the Address model
@customer.shipping_address.street1 # Returns a string, as you would expect
В качестве дополнительного примечания: в данном случае номенклатура «принадлежат к кому-то» вводит в заблуждение, поскольку адреса принадлежат Заказчикам, а не наоборот. Не обращайте внимания на вашу интуицию, хотя; «own_to» используется в зависимости от того, что содержит внешний ключ, который, в нашем случае, как вы увидите, будет обеих моделей. Хах! как это за путаница?
Наконец, мы указываем, что у Клиента много адресов. В этом случае нам не нужно указывать имя класса, с которым связано это свойство, потому что Rails достаточно умен, чтобы увидеть, что у нас есть модель с подходящим именем: 'Address', к которому мы перейдем через секунду. Это позволяет нам получить список всех адресов Клиента, выполнив следующие действия:
@customer.addresses
Это вернет массив экземпляров модели Address, независимо от того, являются ли они адресами выставления счета или доставки. Говоря о модели адреса, вот как это выглядит:
class Address < ActiveRecord::Base
belongs_to :customer
end
Здесь вы выполняете то же самое, что и со строками 'own_to' в модели Customer, за исключением того, что Rails делает для вас немного магии; просматривая имя свойства ('customer'), он видит 'own_to' и предполагает, что это свойство ссылается на модель с тем же именем ('Customer') и что в таблице адресов есть соответствующий столбец ('customer_id') .
Это позволяет нам получить доступ к Клиенту, которому принадлежит Адрес, например:
@address.customer # Returns an instance of the Customer model
@address.customer.first_name # Returns a string, as you would expect