# Association properties

**URL:** <https://www.ruby-forum.com/t/association-properties/48518>\
**Category:** Rails\
**Created:** [November 21, 2005, 8:10pm UTC](https://www.ruby-forum.com/t/association-properties/48518 "2005-11-21T20:10:05Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![leei](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/5fc32e/32.png) [@leei](https://www.ruby-forum.com/u/leei)\
**Post date:** [November 21, 2005, 8:10pm UTC](https://www.ruby-forum.com/t/association-properties/48518/1 "2005-11-21T20:10:05Z")

</div>

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

* * *

---

<div class="post-metadata">

**Author:** ![leei](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/5fc32e/32.png) [@leei](https://www.ruby-forum.com/u/leei)\
**Post date:** [November 21, 2005, 8:54pm UTC](https://www.ruby-forum.com/t/association-properties/48518/2 "2005-11-21T20:54:34Z")

</div>

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](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

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](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

---

<div class="post-metadata">

**Author:** ![leei](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/5fc32e/32.png) [@leei](https://www.ruby-forum.com/u/leei)\
**Post date:** [November 21, 2005, 9:14pm UTC](https://www.ruby-forum.com/t/association-properties/48518/3 "2005-11-21T21:14:44Z")

</div>

Chris H. 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.

---

<div class="post-metadata">

**Author:** ![leei](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/5fc32e/32.png) [@leei](https://www.ruby-forum.com/u/leei)\
**Post date:** [November 21, 2005, 10:43pm UTC](https://www.ruby-forum.com/t/association-properties/48518/4 "2005-11-21T22:43:30Z")

</div>

Chris H. 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.

---

<div class="post-metadata">

**Author:** ![leei](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/5fc32e/32.png) [@leei](https://www.ruby-forum.com/u/leei)\
**Post date:** [November 21, 2005, 10:39pm UTC](https://www.ruby-forum.com/t/association-properties/48518/5 "2005-11-21T22:39:28Z")

</div>

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](http://api.rubyonrails.com/classes/ActiveRecord/Acts/Tree/ClassMethods.html#M000478)

---

<div class="post-metadata">

**Author:** ![leei](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/5fc32e/32.png) [@leei](https://www.ruby-forum.com/u/leei)\
**Post date:** [November 22, 2005, 12:08am UTC](https://www.ruby-forum.com/t/association-properties/48518/6 "2005-11-22T00:08:57Z")

</div>

Maybe this link could be helpful:

[http://wiki.rubyonrails.com/rails/pages/HowToCreateASelfReferentialManyToManyRelationship](http://wiki.rubyonrails.com/rails/pages/HowToCreateASelfReferentialManyToManyRelationship)

---

<div class="post-metadata">

**Author:** ![leei](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/l/5fc32e/32.png) [@leei](https://www.ruby-forum.com/u/leei)\
**Post date:** [November 21, 2005, 11:46pm UTC](https://www.ruby-forum.com/t/association-properties/48518/7 "2005-11-21T23:46:23Z")

</div>

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.
