我希望客户引用两个地址模型,一个用于帐单邮寄地址,另一个用于送货地址。据我了解,外键由其名称决定,如_id。显然我不能将两行命名为address_id(以引用Address表)。我该怎么做?
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
答案 0 :(得分:58)
对于刚接触Rails的人(我最近),这可能会让人感到困惑,因为答案的某些部分发生在您的迁移中,而某些部分发生在您的模型中。此外,您实际上想要模拟两个单独的事物:
地址属于单个客户,每个客户都有许多地址。在您的情况下,这将是1或2个地址,但我鼓励您考虑客户可能有多个送货地址的可能性。例如,我在Amazon.com上有3个单独的送货地址。
另外,我们希望模拟每个客户都有帐单邮寄地址和送货地址这一事实,如果您允许多个送货地址,则可能改为默认送货地址。
以下是您将如何做到这一点:
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”的列。在我们的例子中,他们将每个引用Addresses表中的行,但我们在模型中指定,而不是在迁移中。
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类相关。看到'belongs_to'的Rails将在customers表中查找名为'billing_address_id'的列,我们在上面定义了该列,并使用该列存储外键。然后你就为送货地址做了同样的事情。
这将允许您通过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足够聪明,可以看到我们有一个匹配名称的模型:'Address',我们将在一秒钟内得到它。这允许我们通过执行以下操作获取所有客户地址的列表:
@customer.addresses
这将返回Address模型的实例数组,无论它们是计费还是送货地址。说到地址模型,这就是看起来的样子:
class Address < ActiveRecord::Base
belongs_to :customer
end
在这里,你完成了与Customer模型中'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
答案 1 :(得分:26)
这听起来像是一个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"
答案 2 :(得分:11)
感谢托比,我想出了怎么做:
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
customers表包含shipping_address_id和billing_address_id列。
这实际上是一个has_two关系。我发现this thread也很有帮助。
答案 3 :(得分:4)
我遇到了同样的问题并解决了这个问题:
create_table :customers do |t|
t.integer :address_id, :references => "address"
t.integer :address_id_1, :references => "address"
# other attributes not shown
end
答案 4 :(得分:1)
在Rails 5.1或更高版本中,您可以这样做:
create_table(:customers) do |t|
t.references :address, foreign_key: true
t.references :address1, foreign_key: { to_table: 'addresses' }
end
这将创建字段address_id
和address1_id
,并在数据库级别引用addresses
表
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.define do
factory :customer do
address
association :address1, factory: :address
end
end