
Many to many through a join model
Reported by Brenden Grace | June 30th, 2008 @ 06:37 PM
It appears that currently the only way to get Many To Many associations to work is by letting DM build the join table for you:
class Photo
include DataMapper::Resource
property :id, Serial
has n, :taggings
has n, :tags, :through => Resource
end
class Tag
include DataMapper::Resource
property :id, Serial
has n, :taggings
has n, :photos, :through => Resource
end
The patch included allows this to work:
class Tagging
include DataMapper::Resource
property :photo_id, Integer, :key => true
property :tag_id, Integer, :key => true
belongs_to :photo
belongs_to :tag
end
class Photo
include DataMapper::Resource
property :id, Serial
has n, :taggings
has n, :tags, :through => Resource, :class_name => 'Tagging'
end
class Tag
include DataMapper::Resource
property :id, Serial
has n, :taggings
has n, :photos, :through => Resource, :class_name => 'Tagging'
end
This is my first patch to DM so I wanted to float it past everyone before I went to the trouble of writing specs. Is this the way to go? Is someone already working on this?
http://brendengrace.com/blog/200...
--- dm-core.orig/lib/dm-core/associations/many_to_many.rb 2008-06-30 13:07:10.000000000 -0400
+++ dm-core/lib/dm-core/associations/many_to_many.rb 2008-06-30 18:36:08.000000000 -0400
@@ -38,6 +38,7 @@
EOS
opts = options.dup
+ model_name = opts.delete(:class_name)
opts.delete(:through)
opts[:child_model] ||= opts.delete(:class_name) || Extlib::Inflection.classify(name)
opts[:parent_model] = model.name
@@ -48,16 +49,21 @@
opts[:mutable] = true
names = [ opts[:child_model], opts[:parent_model] ].sort
- model_name = names.join
- storage_name = Extlib::Inflection.tableize(Extlib::Inflection.pluralize(names[0]) + names[1])
+ if model_name
+ storage_name = Extlib::Inflection.tableize(model_name)
+ else
+ model_name = names.join
+ storage_name = Extlib::Inflection.tableize(Extlib::Inflection.pluralize(names[0]) + names[1])
+ end
opts[:near_relationship_name] = Extlib::Inflection.tableize(model_name).to_sym
model.has(model.n, opts[:near_relationship_name])
relationship = model.relationships(repository_name)[name] = RelationshipChain.new(opts)
- unless Object.const_defined?(model_name)
+ if not Object.const_defined?(model_name) or \
+ not Object.const_get(model_name).respond_to?("many_to_many")
model = DataMapper::Model.new(storage_name)
model.class_eval <<-EOS, __FILE__, __LINE__
Comments and changes to this ticket
-
Dan Kubb (dkubb) July 1st, 2008 @ 12:12 AM
I'm wondering why we can't make it so using :through => :taggings will create a regular many to many association, that would seem like the most natural way for it to work from the POV of a casual DM user.
I always thought the plan was to make it so a :through relationship -- where the join model that only contains properties that link to either side of the relationship -- will work the same as our many to many relationship works now.
-
Brenden Grace July 1st, 2008 @ 07:40 AM
My concern would be that one may want to model the join table for some reason. Maybe there are attributes assigned to the association that need to be included for example. It seems based on the docs at http://datamapper.org/docs/assoc... , that the :through => :somethings is used only to point to a table (below).
class Tag include DataMapper::Resource has n, :taggings has n, :photos, :through => :taggings end class Tagging include DataMapper::Resource belongs_to :tag belongs_to :photo end
I had another modification that used :through => ClassName, but that I assumed would be rejected based on the direction the API seems to be going. I simply would like (and need!) DM to support the modeling of join tables so that they can be customized. I like and use the "automagic" joins, but I think we need both options. So how best do we do this?
-
Sam Smoot July 1st, 2008 @ 09:52 AM
- State changed from new to open
:through and many_to_many are supposed to be the same thing.
So before integrating this work, we need to figure out where they're diverging. In your case *Brenden*, :through => :taggings should be what you're asking for. It should inflect the Tagging class from :taggings and use that.
:through => ClassName is acceptable. It's not a DM concern as much as it's a Ruby load-order concern (and we don't use const_missing like AR to load missing constants for you).
So, if you could describe what :through => :taggings (along with defining a Tagging class) doesn't work for you, I'd appreciate it.
-
Brenden Grace July 1st, 2008 @ 10:20 AM
In associations.rb:94
klass = ManyToMany if options[:through] == DataMapper::Resource
This led me to believe that :through => DataMapper::Resource was the only 'blessed' way to do many-to-many.
class Tagging include DataMapper::Resource property :photo_id, Integer, :key => true property :tag_id, Integer, :key => true belongs_to :photo belongs_to :tag end class Photo include DataMapper::Resource property :id, Serial has n, :taggings #has n, :tags, :through => Resource, :class_name => 'Tagging' has n, :tags, :through => :taggings end class Tag include DataMapper::Resource property :id, Serial has n, :taggings #has n, :photos, :through => Resource, :class_name => 'Tagging' has n, :photos, :through => :taggings end
Works until you try to do something like:
tag.photos << Photo.new # Output dm-core-0.9.3/lib/dm-core/associations/one_to_many.rb:229:in `assert_mutable': You can not modify this assocation (DataMapper::Associations::ImmutableAssociationError) from dm-core-0.9.3/lib/dm-core/associations/one_to_many.rb:89:in `<<' from dmtest2.rb:44
I assume that from your comments that this is not the correct behavior and :through => :taggings should be falling into many_to_many.rb.
My only beef with this syntax is if we are going to the trouble of creating a model class, shouldn't we be pointing to that instead of the underlying table that is created by the model class?
-
Dan Kubb (dkubb) January 7th, 2009 @ 04:10 PM
- Tag changed from feature, has_and_belongs_to_many, patch, suggestion to feature, has_and_belongs_to_many, patch
- State changed from open to not-applicable
- Assigned user cleared.
This ticket looks to be a bit old, so I am going to close it with some comments.
Anytime you use :through with the has() command you're asking for a many to many association. When you say :through => Resource, DM will generate a dynamic model for the join. When you say :through => :assoc, it will use the existing association through an explicit join model, which I think is what you want.
Please Sign in or create a free account to add a new ticket.
With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »