Подтвердить что ты не робот

Несколько внешних ключей, ссылающихся на одну и ту же таблицу в RoR

Я хочу, чтобы Клиент ссылался на две модели адресов: одну для платежного адреса и одну для адреса доставки. Как я понимаю, внешний ключ определяется его именем, как _id. Очевидно, я не могу назвать две строки address_id (для ссылки на таблицу адресов). Как мне это сделать?

create_table :customers do |t|
  t.integer :address_id
  t.integer :address_id_1 # how do i make this reference addresses table?
  # other attributes not shown
end
4b9b3361

Ответ 1

Это звучит как отношение has_many ко мне - вместо этого добавьте client_id в таблицу адресов.

Customer
  has_many :addresses

Address
  belongs_to :customer

Вы также можете предоставить внешний ключ и класс в объявлении-сообществе

Customer
   has_one :address
   has_one :other_address, foreign_key => "address_id_2", class_name => "Address"

Ответ 2

Это может быть путаным для людей, новых для Rails (как я был недавно), потому что некоторые части ответа происходят в ваших Migrations и некоторых в ваших моделях. Кроме того, вы действительно хотите моделировать две отдельные вещи:

  • Адрес принадлежит одному клиенту, и каждый клиент имеет много адресов. В вашем случае это будет 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 позаботится об этом для вас, потому что видит, что у вас есть таблица "клиенты", которая соответствует имени столбца (он знает о множественности).

Причина, по которой мы добавили "_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. Рельсы, видя "принадлежность", будут искать столбец в таблице клиентов с именем "billing_address_id", который мы определили выше, и используем этот столбец для хранения внешнего ключа. Затем вы делаете то же самое для адреса доставки.

Это позволит вам получить доступ к вашему платежному адресу и адресу доставки, оба экземпляра модели Address, через экземпляр модели Customer, например:

@customer.billing_address # Returns an instance of the Address model
@customer.shipping_address.street1 # Returns a string, as you would expect

В качестве побочного примечания: номенклатура 'belongs_to' в этом случае путаница, поскольку Адресы принадлежат Клиентам, а не наоборот. Игнорируйте свою интуицию; 'belongs_to' используется в зависимости от того, что содержит внешний ключ, который в нашем случае, как вы увидите, является одновременно и моделями. Хах! как это для смущения?

Наконец, мы указываем, что у Клиента много адресов. В этом случае нам не нужно указывать имя класса, к которому относится это свойство, потому что Rails достаточно умен, чтобы увидеть, что у нас есть модель с соответствующим именем: "Адрес", через секунду мы получим. Это позволяет нам получить список всех адресов клиентов, выполнив следующие действия:

@customer.addresses

Это вернет массив экземпляров модели Address, независимо от того, являются ли они биллинговыми или отправляющими адресами. Говоря о модели Address, вот что это выглядит:

class Address < ActiveRecord::Base
  belongs_to :customer
end

Здесь вы выполняете то же самое, что и с строками 'belongs_to' в модели Customer, за исключением того, что Rails делает для вас какую-то магию; глядя на имя свойства ( "клиент" ), он видит "принадлежность" и предполагает, что это свойство ссылается на модель с тем же именем ( "Клиент" ) и что в таблице адресов имеется соответствующий столбец ( "customer_id" ).

Это позволяет нам получить доступ к Клиенту, который принадлежит адресу:

@address.customer # Returns an instance of the Customer model
@address.customer.first_name # Returns a string, as you would expect

Ответ 3

Я понял, как это сделать благодаря Тоби:

class Address < ActiveRecord::Base
  has_many :customers
end
class Customer < ActiveRecord::Base
  belongs_to :billing_address, :class_name => 'Address', :foreign_key => 'billing_address_id'
  belongs_to :shipping_address, :class_name => 'Address', :foreign_key => 'shipping_address_id'
end

Таблица клиентов включает в себя столбцы shipping_address_id и billing_address_id.

Это, по существу, отношение has_two. Я нашел этот поток полезный.

Ответ 4

У меня была такая же проблема, и я решил сделать это:

create_table :customers do |t|
  t.integer :address_id, :references => "address"
  t.integer :address_id_1, :references => "address"
  # other attributes not shown
end