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/.