Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How best to associate an Address to multiple models in rails?

This question on SO appears to be related to my question, but I'm not sure my question is answered by that.

An address can belong to more than one model (UserProfile and Event) What's the correct way to implement this?

The basic tables:

user_profiles(id)
events(id)

Options for implementing the addresses table:

  1. addresses(id,user_profile_id,event_id)
    This approach seems to be kludgy since if tomorrow the address needs to belong to one more model, I have to add that id field.
    Also, I don't know yet, but adding a new id field may cause some code to break as well ?

  2. addresses(id,model_type,model_id)
    This is polymorphic, right. I'm don't know why, but I feel wary of this somehow?

  3. Some other way to do this?

Note:

I could have made the tables like this, I suppose:

user_profiles(id,address_id)
events(id,address_id)

But, this means that the same address_id can belong to different models. I suppose it should not be that way, because say for example that the address for the event needs to be changed, but it should not affect the address of the user_profile. So that would be something like this (which I think is wrong):

@current_user_profile.address = some_new_address
#this would have changed the address for both the user_profile *and* the event
@current_user_profile.save 
like image 740
Zabba Avatar asked Oct 13 '10 17:10

Zabba


2 Answers

One way would be standard Rails polymorphism:

class Address
  belongs_to :addressable, :polymorphic => true
end

class UserProfile
  has_one address, :as => :addressable
end

class Event
  has_one address, :as => :addressable
end

It could be the nagging feeling you have about this is that you can't create a db-level constraint with Rails-style polymorphic relationships. The alternative (suggested by Dan Chak in Enterprise Rails) is like your #1 option, where you do indeed create a separate id field for each type of relationship. This does leave unused fields, but it also allows for constraints. I can see the argument for both, but the Rails community has been using AR polymorphism for some time with apparently good success. I use it without hesitation. But if that bugs you, you can use Chak's approach. It's a lot more work. : )

EDIT: @Slick86, migration would look something like:

class CreateAddresses < ActiveRecord::Migration
  def change
    create_table :addresses do |t|
      t.integer :addressable_id
      t.string :addressable_type
    end
  end
end
like image 143
Dave Sims Avatar answered Sep 18 '22 23:09

Dave Sims


You missed one option: have a class that contains the common behavior and add the fields to all tables. Use a composed_of aggregate to manage the data.

class Address
  attr_accessor :line1, :line2, :city, :state, :zip

  def initialize(line1, line2, city, state, zip)
    @line1 = line1
  end
end

class UserProfile < ActiveRecord::Base
  composed_of :address, :mapping => %w(line1 line2 city state zip)
end

class Event < ActiveRecord::Base
  composed_of :address, :mapping => %w(line1 line2 city state zip)
end

See #composed_of in the Ruby on Rails API docs.

like image 30
François Beausoleil Avatar answered Sep 19 '22 23:09

François Beausoleil