This project is archived and is in readonly mode.
Polymorphic many to many relationships...
-
Alex
I had a conversation with someone in #rubyonrails and he was surprised to discover that this functionality was not supported.
After talking for a bit, we thought that a few assumptions could be made about a HABTM polymorphic relationship. Whether they are feasible or not is not in my realm of expertise as I am not familiar with the internals of ActiveRecord. I'm going to try and whip together a quick example case of how this might work and some of the thinking behind it:
-=- class Task < ActiveRecord::Base
has_and_belongs_to_many(
:targets, :polymorphic => true)
end
-=- This establishes that Task will be relating to records via a table called "targets_tasks". This table will contain the familiar target_id and target_type columns. As well as a reference to the Task.-=- class Store < ActiveRecord::Base
has_and_belongs_to_many(:tasks, :as => target) end
class Vehicle < ActiveRecord::Base
has_and_belongs_to_many(:tasks, :as => target) end
...and so on and so forth....
-=-
In an application's console:
task = Task.find(69) task.targets [#<Vehicle...>,#<Vehicle...>,#<Store...>,#<Store...>.#<Store...>]
store = Store.find(42) store.tasks [#<Task...>,#<Task...>,#<Task...>,#<Task...>,#<Task...>]
-=-
Trying my best not to make too much light of the complexity involved here, all the information needed is available. We can infer the class of the "target" from the relationship table - which I assume is already being done with the current concept of polymorphic relationships.
This is a highly opinionated concept for implementation which can yield mixed lists with or without single table inheritance. -
Nick
+1 on this - I've come across an instance in one of my applications (stock control) where this would be really useful.
I'm going to try hacking at ActiveRecord to get it to support polymorphic has_and_belongs_to_many /somehow/ (including having one side of the association polymorphic, but the other side not, and both sides polymorphic) - if I get anything useful, I shall submit a patch here.
-
Matt Jones
There's has_many_polymorphs, which does much of what you're describing. You'll also probably have much more luck with an explicit join model and has_many :through rather than habtm, which is something of a second-class citizen in current Rails.
-
Alex
I got some emails that there were updates to this issue...
I ultimately never did get this working. I did also look at has_many_polymorphs, and I should note that it isn't really a solution either.
What I was looking for was a way to define the relationship on the one side and at most the abstract class on the other (and have the subclasses inherit it). Although I figure it should be possible by only making the declaration on the owning side.
(As a loose example...) Issue "has many" Targets
Where Target is an abstract class that is subclassed by things like Cars, Stores, Computers, or whatever.The only declaration in this case would be in Issue where it says it has many Targets. Understandably, something might have to be in Target, so it would have a kind of "belongs to Issue" declaration.
I know this is possible as I did get a rough ORM working in PHP to do this. I followed a lot of the Rails paradigms to get it going.
has_many_polymorphs does little more than provide a shortcut to making declarations you can make in regular Rails anyway. I never saw it as a plugin/extension worth using and it didn't simplify my design.
-
Matt Jones
The issue is that if you're not using STI on Target in the example above, the standard HABTM join record with just two ids isn't sufficient to identify the target record - what table should get the SELECT statement?
This almost works:
class Task < AR::Base has_many :target_links has_many :targets, :through => :target_links, :source => :targetable # => this gives a HasManyThroughAssociationPolymorphicError exception since :source_type isn't specified end
class TargetLink < AR::Base belongs_to :task belongs_to :targetable, :polymorphic => true end
class Target < AR::Base has_many :target_links, :as => :targetable has_many :tasks, :through => :target_links endThe reason the error is thrown in the example above is that the generated SQL wouldn't make sense - what table should be joined against to retrieve records? That's the step that has_many_polymorphs makes possible, via the specification of :from => [:model1, :model2, :model3].
As far as supporting this natively in has_and_belongs_to_many, it doesn't seem likely - 2.3 finally stripped out the (long-deprecated) "decorated join table" stuff with the explicit goal of encouraging more use of has_many :through for this kind of thing.
-
Alex
When I made my solution in PHP, I inferred the table name by the name of the subclass and maintained a table under the name of the "having" class and the abstract "belonging" class which stored the relationships.
I had a table that looked like this:
] TaskTarget o TaskID o ID o TypeI had no way of avoiding (my own MySQL knowledge may have limited this) creating the final graph with separate queries for each subclassed type. I always had to query TaskTarget, get the list sorted by Type and then run a query for each type:
SELECT * FROM Store WHERE ID IN(?,?,?,?)...
This didn't seem like a problem as far as I was concerned as I was querying them by ID (fairly light) and any caching mechanism could easily smooth that over. Considering that you are mixing data and program slightly to accomplish this (a necessary evil), the additional step seemed quite cheap for the capacity to have what I call "blind" relationships with subclasses of a known abstract class...
Very fun to talk about :)
-
Rohit Arondekar
- State changed from new to stale
- Importance changed from to
Marking ticket as stale. If this is still an issue please leave a comment with suggested changes, creating a patch with tests, rebasing an existing patch or just confirming the issue on a latest release or master/branches.