开发者

Can this relationship be described in Ruby on Rails?

Is this a relationship that can be described in Ruby on Rails' ActiveRecord model relationships?

   Customer                          Address
   ===================               =========
   Billing_Address_Id  >------}
                              }---|- AddressId
   Shipping_Address_Id >------}

So that I could have data that looks like this:

Address:

   Id | Addr           | City     | State | Zip   |
   ================================================
   1  | 123 Main       | New York | NY    | 99999 |
   2  | 200 2nd Street | New York | NY    | 99999 |
   3  | 300 3rd Street | Albany   | NY    | 99998 |
   4  | PO Box 4       | Albany   | NY    | 99998 |

Customer:

   Id | Name | Billing_Address_Id | Shipping_Address_Id  |
  开发者_JAVA技巧 =======================================================
   1  | Bob  | 1                  | 1                    |
   2  | Al   | 2                  | 1                    |
   3  | Joe  | 3                  | 4                    |

I want to store addresses in their own table because the data may be shared across customers (shipping address especially). But there would only even be two addresses for any given customer.

I'd like to avoid a many-to-many relationship unless there is no other way.


Yes, it's perfectly possible to do that. Given a customers table with the two foreign keys shipping_address_id and billing_address_id to the addresses table, your Customer model could look like this:

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

This will let a customer reference the same address row for shipping and billing addresses, and will also let several customers share addresses.

Update: When sharing references to addresses like this you'll probably want to carefully consider how to handle address updates. In your example, Bob and Al share the same shipping address. Now, if Bob updates his shipping address you probably want to create a new Address record for Bob's new address rather than update the existing record, to avoid changing Al's address too. Sometimes, you actually might want to update both customers' addresses in this situation, but in most cases you probably don't.


Given table definitions like this:

create_table :addresses do |t|
  t.string :street
  t.string :city
  t.string :state
  t.string :zip
  t.timestamps
end

create_table :customers do |t|
  t.string     :name
  t.references :shipping_address
  t.references :billing_address
  t.timestamps
end

You can associate a billing and shipping address with your customer like this:

class Customer < ActiveRecord::Base
  belongs_to :shipping_address, :class_name => "Address"
  belongs_to :billing_address,  :class_name => "Address"
end


The documentation for ActiveRecord associations has a section on has_one vs belongs_to. In addition, the section on has_one mentions that this should be used only if the other class has the foreign key. So, to model what you want, you would use.

class Address < ActiveRecord::Base
  has_one :shipto_customer, :class_name => "Customer", :foreign_key => "shipping_address_id"
  has_one :billto_customer, :class_name => "Customer", :foreign_key => "billing_address_id"
end

class Customer < ActiveRecord::Base
  belongs_to :shipping_address, :class_name => "Address"
  belongs_to :billing_address,  :class_name => "Address"
end

Example usage:

>> customer = Customer.new(:name => "John Smith",
?>     :shipping_address => Address.new(:address => "123 M St",
?>       :city => "Phoenix", :state => "AZ", :zip => "85015"),
?>     :billing_address => Address.new(:address => "555 W Main Dr",
?>       :city => "Phoenix", :state => "AZ", :zip => "85015")
>>   )
=> #<Customer id: nil, name: "John Smith", billing_address_id: nil, shipping_address_id: nil, created_at: nil, updated_at: nil>
>> customer.save
  Address Create (0.8ms)   INSERT INTO "addresses" ("address", "city", "zip", "created_at", "updated_at", "state") VALUES('555 W Main Dr', 'Phoenix', '85015', '2009-11-14 17:03:28', '2009-11-14 17:03:28', 'AZ')
  Address Create (0.2ms)   INSERT INTO "addresses" ("address", "city", "zip", "created_at", "updated_at", "state") VALUES('123 M St', 'Phoenix', '85015', '2009-11-14 17:03:28', '2009-11-14 17:03:28', 'AZ')
  Customer Create (0.2ms)   INSERT INTO "customers" ("name", "billing_address_id", "shipping_address_id", "created_at", "updated_at") VALUES('John Smith', 1, 2, '2009-11-14 17:03:28', '2009-11-14 17:03:28')
=> true
>> customer.shipping_address
=> #<Address id: 2, address: "123 M St", city: "Phoenix", state: "AZ", zip: "85015", created_at: "2009-11-14 17:03:28", updated_at: "2009-11-14 17:03:28">
>> customer.billing_address
=> #<Address id: 1, address: "555 W Main Dr", city: "Phoenix", state: "AZ", zip: "85015", created_at: "2009-11-14 17:03:28", updated_at: "2009-11-14 17:03:28">
>> 
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜