RoR 中引用同一个表的多个外键

发布于 2024-08-19 15:54:45 字数 282 浏览 2 评论 0原文

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

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?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

说好的呢 2024-08-26 15:54:45

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

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

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

执行此操作的方法如下:

迁移

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 会为你处理这个问题,因为它发现你有一个表“customers”,它与列名匹配(它知道复数)。

我们向客户迁移添加“_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 看到“belongs_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

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

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

@customer.addresses

这将返回 Address 模型的实例数组,无论它们是账单地址还是送货地址。说到地址模型,它看起来像这样:

class Address < ActiveRecord::Base
  belongs_to :customer
end

在这里,您将完成与客户模型中的“belongs_to”行完全相同的事情,只不过 Rails 为您做了一些魔法;查看属性名称(“customer”),它会看到“belongs_to”,并假设该属性引用具有相同名称(“Customer”)的模型,并且地址表上有一个匹配的列(“customer_id”) 。

这使我们能够访问地址所属的客户,如下所示:

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

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:

  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 default shipping address if you allow more than one shipping address.

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.

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).

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.

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.

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 # 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 both models. Hah! how's that for confusing?

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.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:

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').

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
愚人国度 2024-08-26 15:54:45

对我来说,这听起来像是 has_many 关系 - 将 customer_id 放入地址表中。

Customer
  has_many :addresses

Address
  belongs_to :customer

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

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

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

Customer
  has_many :addresses

Address
  belongs_to :customer

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

Customer
   has_one :address
   has_one :other_address, foreign_key => "address_id_2", class_name => "Address"
大姐,你呐 2024-08-26 15:54:45

感谢托比,我知道了如何做到这一点:

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 关系。我发现此帖子也很有帮助。

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.

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

大姐,你呐 2024-08-26 15:54:45

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

迁移

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

这将创建字段 address_idaddress1_id 并使数据库级别引用 addresses table

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

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

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

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

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_id and make the database level references to the addresses table

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.define do
  factory :customer do
    address
    association :address1, factory: :address
  end
end
禾厶谷欠 2024-08-26 15:54:45

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

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文