This is good timing. I need to know about this stuff tomorrow to help a colleague out. Rails Polymorphic Associations were a bit mysterious to me and so I didn't use them when I first started with Rails but the time has come to put them to work.
First, the scenario:
A Player and a Corporation both may have many Wallet Balance Entries and a Wallet Balance Entry belongs to either a Player or a Corporation.
The Models are this:
-
# app/models/wallet_balance_entry.rb
-
class WalletBalanceEntry <ActiveRecord::Base
-
belongs_to :wallet_balance_owner, :polymorphic => true
-
end
And (just the Player model)
-
# app/models/player.rb
-
class Player <ActiveRecord::Base
-
has_many :wallet_balance_entries, :as => :wallet_balance_owner
-
end
The relevant bits of the WalletBalanceEntry schema look like this:
-
create_table "wallet_balance_entries", :force => true do |t|
-
t.string "wallet_balance_owner_type"
-
t.integer "wallet_balance_owner_id"
Note that wallet_balance_owner_type and wallet_balance_owner_id match the name that the Player model specifies with :as => :wallet_balance_owner. The Corporation model will have the exact same line and it will work! Should Alliances come to have many wallet balance entries the Alliance model will have this same line as well.


I used to use polymorphic associations a lot but when a new programmer joined our team, he made me remove them all. In fact, we started again but that’s another story. I have now seen the error of my ways. By using them, we were unable to add key constraints to the database and thereby risked the integrity of our data. I thought not having them would be a pain but actually it’s not as bad as I thought. I’d much rather keep our data intact. For smaller apps, they’re probably fine but I suggest kicking the habit before it starts. (-;
Comment by James Le Cuirot — August 16, 2007 @ 01:35
Database integrity in a rails app can be done with the many validations (see http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html ). I prefer not to use in-database foreign key constraints because it requires a lot more error handling for ActiveRecord::RecordInvalid when record with a foreign key is destroyed. The Rails way is to specify the association in the model and include :dependent => :destroy on the association.
Data integrity is fine with Rails as long as Rails is the funnel to your data.
Comment by Lisa Seelye — August 16, 2007 @ 08:00
I suppose that’s true enough if you’re using a database that supports transactions. My friend just didn’t trust Rails that much! I’ve always been wary of threading issues though I gather that Mongrel is single-threaded by default. I’ve only started using it recently.
Comment by James Le Cuirot — August 16, 2007 @ 11:07