Polymorphic associations, wrong use?

Hello,

I would like to populate dropdown list with values from two different
models. My first idea was to create a polymorphic association. But
then I got stuck with the code for a dropdown list. How to make it
return object_id and object_type?

Here are my models:

card.rb -----------------------------------------------
class Card < ActiveRecord::Base
belongs_to :vehicle, :polymorphic => true
end

car.rb -----------------------------------------------
class Car < ActiveRecord::Base
has_many :cards, :as => :vehicle
end

boat.rb --------------------------------------------
class Boat < ActiveRecord::Base
has_many :cards, :as => :vehicle
end

I would like to have a dropdown list in views/cards/new.rhtm that
would add either a Car or a Boat to a Card object. To keep it simple
say Car and Boat have just two methods ‘name’ and ‘id’. And I would
like to have ‘name’ appearing the the dropdown list. Is it the right
approach?

Many thanks

Piotr

No, wouldn’t work, since a car and a boat can have the same id.

You can though use something like name and car_id or boat_id
as identifier (like car_123 and boat_123)
Then in you controller params[:id] (or whatever) would contain that
and you could use some string functions to split at the _ the
type and id

On Aug 27, 12:35 am, Thorsten Müller [email protected] wrote:

No, wouldn’t work, since a car and a boat can have the same id.

You can though use something like name and car_id or boat_id
as identifier (like car_123 and boat_123)
Then in you controller params[:id] (or whatever) would contain that
and you could use some string functions to split at the _ the
type and id

This is a good example of one of the problems with the polymorphic
model as it is commonly implemented (not only in Rails solutions).
String functions to split the type and id? Frankly, that is a kludge
that is going to lead to interesting issues as you try to do more with
your data. I’d suggest you consider that your underlying logical model
is not correct for your requirements. Try modeling Car and Boat as
subtypes of Vehicle, which would be the supertype. A Card would then
have vehicle_id as a foreign key (unless cards can belong to more than
one vehicle, in which case you need a join model between Vehicle and
Card). At the implementation level, Vehicle could have a database
table (and model) of its own. The Cars and Boats tables would then
each have vehicle_id as a foreign key. You would also be able to use
foreign key constraints in Cars and Boats to help ensure the integrity
of your data - the polymorphic association method Rails uses does not
allow for these constraints, which is a serious problem unless you
don’t need to worry about the reliability of the data in your database
(but if you can’t rely on the integrity of the data, what use is your
database?). For more information, see “Case*Method: Entity
Relationship Modelling” by Richard Barker:
http://www.amazon.com/Case-Method-Entity-Relationship-Modelling/dp/0201416964

On Aug 28, 11:15 am, smeade [email protected] wrote:

STI better suits what you’re asking about doing:

What “better suits” can only be determined by a proper analysis of the
business requirements. It might appear to solve the one issue that has
been presented here, but is it the appropriate solution in the wider
context of the whole system? The supertype-subtype model I suggested
as a possibility is a well-known pattern that can be used in
situations like this.

Single-Table Inheritance will result in a table with lots of NULLs in
it. You then have the issue of ensuring that the columns for one model
are filled, while columns for other models have NULL in them. On the
subject of NULLs, see:

If you decide STI is appropriate for your needs, this might help get
you started:

Or see the Agile Web D. with Rails book, of course.

STI better suits what you’re asking about doing:

card.rb

class Card < ActiveRecord::Base
belongs_to :vehicle
end

vehicle.rb

class Vehicle < ActiveRecord::Base
has_many :cards
end

car.rb

class Car < Vehicle
end

boat.rb

class Boat < Vehicle
end

In your views, the relationship between cards and cars or boats is
built by filling in vehicle id just like any other attribute.

<%= f.collection_select :vehicle_id, Vehicle.find(:all), ‘id’,
‘name’, :include_blank => true %>

Clean and easy.