Rails模型和DB模式,具有来自同一个表的两个外键

时间:2010-05-03 07:56:04

标签: ruby-on-rails activerecord

我正在为Rails应用程序开发订单模型。我正在尝试将具有BillToAddressId和ShipToAddressId的订单表示为来自Address表的外键。

地址表如下:

create_table :addresses do |t|
      t.string :country
      t.string :state
      t.string :city
      t.string :zipcode
      t.string :line1
      t.timestamps

我是Rails的新手,所以我不确定如何在DB / migrate中为Order和Address表示这一点。

如果有人可以指导我构建模型和迁移脚本,那就太好了。

2 个答案:

答案 0 :(得分:4)

你可以用以下方式完成

class Order < ActiveRecord::Base
  belongs_to :bill_to, :class_name => 'Address', :foreign_key => 'BillToAddressId'
  belongs_to :ship_to, :class_name => 'Address', :foreign_key => 'ShipToAddressId'
end

用于Ruby Naming Conevention 列名“ShipToAddressId”必须为“ship_to_address_id”

答案 1 :(得分:0)

重要提示:您不应允许用户删除或修改地址,因为您可以使用订单和地址之间关系建模的方式。如果这样做,已经发生的订单上的结算或送货地址最终会被修改或删除,这是一件非常糟糕的事情。为避免这种情况,只允许用户添加新地址或将现有地址标记为非活动状态。

有了这样的话,这就是你如何在Rails中做到这一点(也就是说,做你所说的,而不是我在上面段落中概述的那样):

迁移

class CreateOrders < ActiveRecord::Migration
  create_table :orders 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”的列。在我们的例子中,他们将每个引用Users表中的行,但我们在模型中指定,而不是在迁移中。

模型

class Order < ActiveRecord::Base
  belongs_to :billing_address, class_name => 'Address'
  belongs_to :shipping_address, class_name => 'Address'
end

在这里,您要在Order模型上创建一个名为:billing_address的属性,然后指定此属性将引用Address类的实例。看到'belongs_to'的Rails将在订单表中查找名为'billing_address_id'的列,我们在上面定义了该列,并使用它来存储外键。然后你就为送货地址做了同样的事情。

这将允许您通过Order模型的实例访问您的Billing Address和Shipping Address(地址模型的两个实例),如下所示:

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

作为旁注:在这种情况下,'belongs_to'命名法有点令人困惑,因为订单不属于地址。但是要忽略你的直觉; 'belongs_to'用于包含外键的任何内容。

class Address < ActiveRecord::Base
  has_many :orders_as_billing_address, :class_name => 'Order', :foreign_key => 'billing_address_id'
  has_many :orders_as_shipping_address, :class_name => 'Order', :foreign_key => 'shipping_address_id'
end

在这里,您要在地址模型上创建一个名为:orders_as_billing_address的属性,指定此属性与订单模型相关,并且Order模型上与此属性相关的外键称为“billing_address_id”。然后你正在做同样的事情:orders_as_shipping_address。

这样您就可以获得使用地址作为结算或送货地址的所有订单,如下所示:

@address.orders_as_billing_address
@address.orders_as_shipping_address

执行其中任何一项操作都将返回Order模型的实例数组。