One of the great advantages of relational modelling (over typical object modelling) is the ability to easily extend an association (i.e. an association table) with properties of the association itself. In general O/R mappings make this very difficult to access or take advantage of. RoR certainly makes the association properties available by grabbing the association properties due to their participation in the join, but I haven''t yet figured out how to manage update of these properties. An example of this is below. The question is, how can I update ''indirect_count'', ''created_at'' and `created_by` properties of the association table when I add a member to a group? Do I need to add the proper instance variables to the Actor object that I am adding to the members set? Or do I need to go to the SQL itself? Maybe this should be a FAQ? --- MySQL --- CREATE TABLE `users` ( `id` int(11) NOT NULL auto_increment, `type` varchar(16) default ''null'', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `group_members` ( `group_id` int(11) NOT NULL default ''0'', `member_id` int(11) NOT NULL default ''0'', `indirect_count` int(11) NOT NULL default ''0'', `created_at` datetime NOT NULL default ''0000-00-00 00:00:00'', `created_by` int(11) NOT NULL default ''0'', KEY `group_id` (`group_id`), KEY `member_id` (`member_id`), KEY `created_by` (`created_by`), CONSTRAINT `group_members_ibfk_3` FOREIGN KEY (`member_id`) REFERENCES `users` (`id`), CONSTRAINT `group_members_ibfk_4` FOREIGN KEY (`group_id`) REFERENCES `users` (`id`) CONSTRAINT `group_members_ibfk_5` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 --- actor.rb --- class Actor < ActiveRecord::Base set_table_name ''users'' has_and_belongs_to_many :groups, :join_table => ''group_members'', :foreign_key => ''member_id'' end class Person < Actor end class Group < Actor has_and_belongs_to_many :members, :class_name => ''Actor'', :join_table => ''group_members'' end ----
I think maybe you are overcomplicating it...here''s a simple example of (i think) what you are trying to accomplish. It''s possible I''m not understanding as well, so correct me if I am wrong. users ---------- id name created_at groups ---------- id name creator_id created_at groups_users (join table) ---------- group_id user_id date_joined class User < ActiveRecord::Base has_and_belongs_to_many :groups def join_group(group, creator) groups.push_with_attributes(group, :date_joined => Time.now, :creator_id => creator.id <http://creator.id>) end end class Group < ActiveRecord::Base has_and_belongs_to_many :users def add_user(user, creator) users.push_with_attributes(user, :date_joined => Time.now, :creator_id => creator.id <http://creator.id>) end end this way you can do: user = User.create(:name => "Bob") group = Group.create(:name => "Geeks-R-Us") # add a user/group association user.join_group(group, user) # or group.add_user(user, user) # how many users in each group groups = Group.find(:all) groups.each do { |g| puts "group: #{g.name <http://g.name>}, members: #{ g.users.size}" } # how many groups does user belong to user.groups.size # remove a user/group association user.groups.delete(group) # or group.users.delete(user) hope this helps On 11/21/05, Lee Iverson <leei-1KtHxNhk3+Ww5LPnMra/2Q@public.gmane.org> wrote:> > One of the great advantages of relational modelling (over typical object > modelling) is the ability to easily extend an association (i.e. an > association table) with properties of the association itself. In > general O/R mappings make this very difficult to access or take > advantage of. RoR certainly makes the association properties available > by grabbing the association properties due to their participation in the > join, but I haven''t yet figured out how to manage update of these > properties. > > An example of this is below. The question is, how can I update > ''indirect_count'', ''created_at'' and `created_by` properties of the > association table when I add a member to a group? Do I need to add the > proper instance variables to the Actor object that I am adding to the > members set? Or do I need to go to the SQL itself? Maybe this should > be a FAQ? > > --- MySQL --- > CREATE TABLE `users` ( > `id` int(11) NOT NULL auto_increment, > `type` varchar(16) default ''null'', > PRIMARY KEY (`id`) > ) ENGINE=InnoDB DEFAULT CHARSET=utf8; > > CREATE TABLE `group_members` ( > `group_id` int(11) NOT NULL default ''0'', > `member_id` int(11) NOT NULL default ''0'', > `indirect_count` int(11) NOT NULL default ''0'', > `created_at` datetime NOT NULL default ''0000-00-00 00:00:00'', > `created_by` int(11) NOT NULL default ''0'', > KEY `group_id` (`group_id`), > KEY `member_id` (`member_id`), > KEY `created_by` (`created_by`), > CONSTRAINT `group_members_ibfk_3` FOREIGN KEY (`member_id`) REFERENCES > `users` (`id`), > CONSTRAINT `group_members_ibfk_4` FOREIGN KEY (`group_id`) REFERENCES > `users` (`id`) > CONSTRAINT `group_members_ibfk_5` FOREIGN KEY (`created_by`) > REFERENCES `users` (`id`) > ) ENGINE=InnoDB DEFAULT CHARSET=utf8 > > --- actor.rb --- > class Actor < ActiveRecord::Base > set_table_name ''users'' > has_and_belongs_to_many :groups, :join_table => ''group_members'', > :foreign_key => ''member_id'' > end > > class Person < Actor > end > > class Group < Actor > has_and_belongs_to_many :members, :class_name => ''Actor'', :join_table > => ''group_members'' > end > > ---- > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Chris Hall wrote:> I think maybe you are overcomplicating it...here''s a simple example of > (i think) what you are trying to accomplish. It''s possible I''m not > understanding as well, so correct me if I am wrong. >Actually, I''m not overcomplicating it, but ''push_with_attributes'' is the right answer it seems. Since a Group can contain other groups, I need the inheritance I showed. Thanks for the pointer. It''s still a bit annoying that I have to make explicit reference to ''creator_id'', but that''s the price of putting this information into the association table.> class User < ActiveRecord::Base > has_and_belongs_to_many :groups > > def join_group(group, creator) > groups.push_with_attributes(group, :date_joined => Time.now, > :creator_id => creator.id <http://creator.id>) > end > end > > class Group < ActiveRecord::Base > has_and_belongs_to_many :users > > def add_user(user, creator) > users.push_with_attributes(user, :date_joined => Time.now, > :creator_id => creator.id <http://creator.id>) > end > end
if a group can belong to group, take a look at acts_as_tree: groups ---------- id parent_id (can be null) name class Group < ActiveRecord::Base has_and_belongs_to_many :users acts_as_tree ... end root_group = group.create(:name => "Root Group") child_group = group_a.children.create(:name => "Child Group") acts_as_tree ==> http://api.rubyonrails.com/classes/ActiveRecord/Acts/Tree/ClassMethods.html#M000478 On 11/21/05, Lee Iverson <leei-1KtHxNhk3+Ww5LPnMra/2Q@public.gmane.org> wrote:> > Chris Hall wrote: > > > I think maybe you are overcomplicating it...here''s a simple example of > > (i think) what you are trying to accomplish. It''s possible I''m not > > understanding as well, so correct me if I am wrong. > > > Actually, I''m not overcomplicating it, but ''push_with_attributes'' is the > right answer it seems. Since a Group can contain other groups, I need > the inheritance I showed. Thanks for the pointer. It''s still a bit > annoying that I have to make explicit reference to ''creator_id'', but > that''s the price of putting this information into the association table. > > > class User < ActiveRecord::Base > > has_and_belongs_to_many :groups > > > > def join_group(group, creator) > > groups.push_with_attributes(group, :date_joined => Time.now, > > :creator_id => creator.id <http://creator.id> <http://creator.id>) > > end > > end > > > > class Group < ActiveRecord::Base > > has_and_belongs_to_many :users > > > > def add_user(user, creator) > > users.push_with_attributes(user, :date_joined => Time.now, > > :creator_id => creator.id <http://creator.id> <http://creator.id>) > > end > > end > > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Chris Hall wrote:> if a group can belong to group, take a look at acts_as_tree:Yes, but a group (or user) can itself potentially belong to many other groups... Trust me, I do know what I''m modelling.
Can you specify the operations you want to do with some fixtures and test cases? fixtures: G 1,2 (group 1 belongs to group 2) G 1,3 G 4,3 G 4,5 U 6,4 (user 6 belongs to group 4) tests: UserBelongsToGroup(6,1)=false UserBelongsToGroup(6,2)=false UserBelongsToGroup(6,3)=true UserBelongsToGroup(6,4)=true UserBelongsToGroup(6,5)=true UsersGroups(6)=[3,4,5] (All groups a user belongs to) GroupsUsers(3)=[6] (All users belonging to a group) The tables are ok, presuming Type is used to separate between user and group. After your confirmation, it''s possible to dig into implementation. -- Posted via http://www.ruby-forum.com/.