Ruby-on-rails 在 RoR 中引用同一个表的多个外键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2166613/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:13:09  来源:igfitidea点击:

Multiple foreign keys referencing the same table in RoR

ruby-on-railsmigration

提问by titaniumdecoy

I want a Customer to reference two Address models, one for the billing address and one for the shipping address. As I understand it, the foreign key is determined by its name, as _id. Obviously I can't name two rows address_id (to reference the Address table). How would I do this?

我希望客户引用两种地址模型,一种用于帐单地址,一种用于送货地址。据我了解,外键由其名称确定,如_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

采纳答案by Toby Hede

This sounds like a has_many relationship to me - put the customer_id in the Address table instead.

这对我来说听起来像是 has_many 关系 - 将 customer_id 放在 Address 表中。

Customer
  has_many :addresses

Address
  belongs_to :customer

You can also provide a foreign key and class in the assoc declaration

您还可以在 assoc 声明中提供外键和类

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

回答by Richard Jones

This can be kind of confusing to people new to Rails (as I was recently), because some parts of the answer take place in your Migrations and some in your Models. Also, you actually want to model two separate things:

这可能会让 Rails 的新手感到困惑(就像我最近一样),因为答案的某些部分发生在您的迁移中,而有些发生在您的模型中。此外,您实际上想要对两个独立的事物进行建模:

  1. An address belongs to a single customer and each customer has many addresses. In your case this would be either 1 or 2 addresses, but I would encourage you to consider the possibility that a customer can have more than one shipping address. As an example, I have 3 separate shipping addresses with Amazon.com.

  2. Separately, we want to model the fact that each customer has a billing address and a shipping address, which might instead be the defaultshipping address if you allow more than one shipping address.

  1. 一个地址属于一个客户,每个客户有多个地址。在您的情况下,这可能是 1 个或 2 个地址,但我鼓励您考虑客户可能有多个送货地址的可能性。例如,我在 Amazon.com 上有 3 个单独的送货地址。

  2. 另外,我们希望对每个客户都有一个帐单地址和一个送货地址这一事实进行建模,如果您允许多个送货地址,则这可能是默认送货地址。

Here's how you would do that:

以下是你将如何做到这一点:

Migrations

迁移

class CreateCustomers < ActiveRecord::Migration
  create_table :customers do |t|
    def up
      t.references :billing_address
      t.references :shipping_address
    end
  end
end

Here you are specifying that there are two columns in this table that will be referred to as :billing_address and :shipping_address and which hold references to another table. Rails will actually create columns called 'billing_address_id' and 'shipping_address_id' for you. In our case they will each reference rows in the Addresses table, but we specify that in the models, not in the migrations.

此处您指定此表中有两列将被称为 :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

Here you are also creating a column that references another table, but you are omitting the "_id" at the end. Rails will take care of that for you because it sees that you have a table, 'customers', that matches the column name (it knows about plurality).

在这里,您还创建了一个引用另一个表的列,但最后省略了“_id”。Rails 会为您处理这个问题,因为它会发现您有一个与列名匹配的表“customers”(它知道复数)。

The reason we added "_id" to the Customers migration is because we don't have a "billing_addresses" or "shipping_addresses" table, so we need to manually specify the entire column name.

我们在客户迁移中添加“_id”的原因是因为我们没有“billing_addresses”或“shipping_addresses”表,因此我们需要手动指定整个列名。

Models

楷模

class Customer < ActiveRecord::Base
  belongs_to :billing_address, :class_name => 'Address'
  belongs_to :shipping_address, :class_name => 'Address'
  has_many :addresses
end

Here you are creating a property on the Customer model named :billing_address, then specifying that this property is related to the Address class. Rails, seeing the 'belongs_to', will look for a column in the customers table called 'billing_address_id', which we defined above, and use that column to store the foreign key. Then you're doing the exact same thing for the shipping address.

在这里,您将在 Customer 模型上创建一个名为 :billing_address 的属性,然后指定该属性与 Address 类相关。Rails 看到“belongs_to”,将在客户表中查找名为“billing_address_id”的列(我们在上面定义),并使用该列存储外键。然后你对送货地址做同样的事情。

This will allow you to access your Billing Address and Shipping Address, both instances of the Address model, through an instance of the Customer model, like this:

这将允许您通过 Customer 模型的实例访问您的 Billing Address 和 Shipping Address,这两个都是 Address 模型的实例,如下所示:

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

As a side note: the 'belongs_to' nomenclature is kind of confusing in this case, since the Addresses belong to the Customers, not the other way around. Ignore your intuition though; the 'belongs_to' is used on whichever thing contains the foreign key which, in our case, as you will see, is bothmodels. Hah! how's that for confusing?

附带说明:在这种情况下,“belongs_to”命名法有点令人困惑,因为地址属于客户,而不是相反。忽略你的直觉;'belongs_to' 用于包含外键的任何事物,在我们的例子中,正如您将看到的,这两个模型都是。哈!怎么会糊涂?

Finally, we are specifying that a Customer has many addresses. In this case, we don't need to specify the class name this property is related to because Rails is smart enough to see that we have a model with a matching name: 'Address', which we'll get to in a second. This allows us to get a list of all of Customer's addresses by doing the following:

最后,我们指定一个 Customer 有许多地址。在这种情况下,我们不需要指定与此属性相关的类名,因为 Rails 足够聪明,可以看到我们有一个具有匹配名称的模型:'Address',我们将在稍后介绍。这使我们能够通过执行以下操作来获取所有客户地址的列表:

@customer.addresses

This will return an array of instances of the Address model, regardless of whether they are billing or shipping addresses. Speaking of the Address model, here's what that looks like:

这将返回一个 Address 模型的实例数组,无论它们是账单地址还是送货地址。说到地址模型,下面是它的样子:

class Address < ActiveRecord::Base
  belongs_to :customer
end

Here you're accomplishing the exact same thing as with the 'belongs_to' lines in the Customer model, except that Rails does some magic for you; looking at the property name ('customer'), it sees the 'belongs_to' and assumes that this property references the model with the same name ('Customer') and that there is a matching column on the addresses table ('customer_id').

在这里,您完成了与 Customer 模型中的 'belongs_to' 行完全相同的事情,只是 Rails 为您做了一些魔术;查看属性名称 ('customer'),它会看到 'belongs_to' 并假定该属性引用具有相同名称 ('Customer') 的模型,并且地址表 ('customer_id') 中有一个匹配的列.

This allows us to access the Customer that an Address belongs to like this:

这允许我们像这样访问地址所属的客户:

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

回答by titaniumdecoy

I figured out how to do it thanks to Toby:

由于托比,我想出了如何做到这一点:

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

The customers table includes shipping_address_id and billing_address_id columns.

客户表包括 shipping_address_id 和 billing_address_id 列。

This is essentially a has_two relationship. I found this threadhelpful as well.

这本质上是一个 has_two 关系。我发现这个线程也很有帮助。

回答by Mateus

I had the same problem and solved doing this:

我遇到了同样的问题并解决了这个问题:

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

回答by Toby 1 Kenobi

In Rails 5.1 or greater you can do it like this:

在 Rails 5.1 或更高版本中,您可以这样做:

Migration

移民

create_table(:customers) do |t|
    t.references :address, foreign_key: true
    t.references :address1, foreign_key: { to_table: 'addresses' }
end

This will create the fields address_id, and address1_idand make the database level references to the addressestable

这将创建字段address_id,并对表address1_id进行数据库级引用。addresses

Models

楷模

class Customer < ActiveRecord::Base
  belongs_to :address
  belongs_to :address1, class_name: "Address"
end

class Address < ActiveRecord::Base
  has_many :customers,
  has_many :other_customers, class_name: "Customer", foreign_key: "address1_id"
end

FactoryBot

工厂机器人

If you uses FactoryBot then your factory might look something like this:

如果您使用 FactoryBot,那么您的工厂可能如下所示:

FactoryBot.define do
  factory :customer do
    address
    association :address1, factory: :address
  end
end