# Problem with has\_and\_belongs\_to\_many

**URL:** <https://www.ruby-forum.com/t/problem-with-has-and-belongs-to-many/128709>\
**Category:** Rails\
**Created:** [February 10, 2008, 8:23pm UTC](https://www.ruby-forum.com/t/problem-with-has-and-belongs-to-many/128709 "2008-02-10T20:23:30Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Felix\_N](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/c57346/32.png) [@Felix\_N](https://www.ruby-forum.com/u/Felix_N)\
**Post date:** [February 10, 2008, 8:23pm UTC](https://www.ruby-forum.com/t/problem-with-has-and-belongs-to-many/128709/1 "2008-02-10T20:23:30Z")

</div>

hello,

I have an m:n relationsship (has\_and\_belongs\_to\_many) between the models  
CoreUser and CoreGroup. So from some rails experience I thought I’d  
be able to add a (user,group)-relation simply by doing  
agroup.core\_users \<\< auser.

Unfortunately the following test-case fails on the last assertion  
(unless I include the line that is commented out):

def test\_add\_remove  
someuser = CoreUser.create!(:username =\> ‘test2s’, …)  
ag = CoreGroup.find\_by\_path("/Administrators")  
assert !ag.core\_users.include?(someuser)  
assert !someuser.core\_groups.include?(ag)

```
ag.core_users << someuser

```

# someuser.core\_groups \<\< ag

```
assert ag.core_users.include?(someuser)
assert someuser.core_groups.include?(ag)

```

end

Shouldn’t it work with the line being commented out (one direction)?

The models look like this:

class CoreUser \< ActiveRecord::Base

has\_one :core\_page

has\_many :core\_artifacts  
has\_many :core\_rating1s  
has\_many :core\_rating2s  
has\_many :core\_messages  
has\_many :core\_widgets  
has\_and\_belongs\_to\_many :core\_groups

validates\_presence\_of :username, :first\_name, :last\_name,  
:email\_address, :password\_hash  
validates\_uniqueness\_of :username, :email\_address  
[…]

class CoreGroup \< ActiveRecord::Base

has\_one :core\_page

has\_and\_belongs\_to\_many :core\_users  
acts\_as\_tree :order =\> :name # sort children by :name

validates\_presence\_of :name  
validates\_uniqueness\_of :name, :scope =\> :parent\_id

thanks,

–  
Felix N.

---

<div class="post-metadata">

**Author:** ![Felix\_N](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/c57346/32.png) [@Felix\_N](https://www.ruby-forum.com/u/Felix_N)\
**Post date:** [February 10, 2008, 9:33pm UTC](https://www.ruby-forum.com/t/problem-with-has-and-belongs-to-many/128709/2 "2008-02-10T21:33:33Z")

</div>

Felix N.  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> def test\_add\_remove
> 
> has\_many :core\_rating1s
> 
> has\_one :core\_page
> 
> has\_and\_belongs\_to\_many :core\_users  
> acts\_as\_tree :order =\> :name # sort children by :name
> 
> validates\_presence\_of :name  
> validates\_uniqueness\_of :name, :scope =\> :parent\_id

here is some additional information from a console session:

mysql\> select \* from core\_groups\_core\_users;  
±--------------±-------------+  
| core\_group\_id | core\_user\_id |  
±--------------±-------------+  
| 1 | 1 |  
| 2 | 2 |  
| 2 | 3 |  
| 2 | 4 |  
| 4 | 2 |  
| 5 | 5 |  
| 5 | 6 |  
| 5 | 7 |  
| 5 | 8 |  
| 9 | 7 |  
| 6 | 2 |  
| 7 | 2 |  
| 6 | 3 |  
| 8 | 3 |  
| 12 | 6 |  
| 12 | 5 |  
| 10 | 3 |  
| 11 | 3 |  
| 10 | 4 |  
| 11 | 4 |  
| 17 | 8 |  
| 13 | 2 |  
| 14 | 2 |  
| 13 | 3 |  
| 15 | 3 |  
| 13 | 4 |  
| 16 | 4 |  
±--------------±-------------+  
27 rows in set (0.00 sec)

> > u.core\_groups  
> > =\> [#\<CoreGroup id: 1, parent\_id: nil, type: “”, name: “Administrators”,  
> > is\_open: false, is\_system\_group: true, rights: “”,  
> > serialized\_custom\_data: “BAh7AA==\n”, created\_at: “2008-02-10 20:25:44”,  
> > updated\_at: “2008-02-10 20:25:44”\>]  
> > g.core\_users  
> > =\> [#\<CoreUser id: 5, username: “mkaul2m”, first\_name: “Manfred”,  
> > last\_name: “Kaul”, email\_address: “removed\_email\_address@domain.invalid”,  
> > password\_hash: “5ebe2294ecd0e0f08eab7690d2a6ee69”, rights: “”,  
> > serialized\_custom\_data: “BAh7AA==\n”, num\_logins: 0, last\_login: nil,  
> > created\_at: “2008-02-10 20:25:44”, updated\_at: “2008-02-10 20:25:44”\>,  
> > #\<CoreUser id: 6, username: “sbuers2m”, first\_name: “Simone”, last\_name:  
> > “Bürsner”, email\_address: “removed\_email\_address@domain.invalid”, password\_hash:  
> > “e8636ea013e682faf61f56ce1cb1ab5c”, rights: “”, serialized\_custom\_data:  
> > “BAh7AA==\n”, num\_logins: 0, last\_login: nil, created\_at: “2008-02-10  
> > 20:25:44”, updated\_at: “2008-02-10 20:25:44”\>, #\<CoreUser id: 7,  
> > username: “tbreue2m”, first\_name: “Thomas”, last\_name: “Breuer”,  
> > email\_address: “removed\_email\_address@domain.invalid”, password\_hash:  
> > “699a474e923b8da5d7aefbfc54a8a2bd”, rights: “”, serialized\_custom\_data:  
> > “BAh7AA==\n”, num\_logins: 0, last\_login: nil, created\_at: “2008-02-10  
> > 20:25:44”, updated\_at: “2008-02-10 20:25:44”\>, #\<CoreUser id: 8,  
> > username: “vdhude2m”, first\_name: “Marlis”, last\_name: “von der Hude”,  
> > email\_address: “removed\_email\_address@domain.invalid”, password\_hash:  
> > “d1cf6a6090003989122c4483ed135d55”, rights: “”, serialized\_custom\_data:  
> > “BAh7AA==\n”, num\_logins: 0, last\_login: nil, created\_at: “2008-02-10  
> > 20:25:44”, updated\_at: “2008-02-10 20:25:44”\>]

=\> neither u is in g or g contains u

> > g.core\_users \<\< u

mysql\> select \* from core\_groups\_core\_users;  
±--------------±-------------+  
| core\_group\_id | core\_user\_id |  
±--------------±-------------+  
| 1 | 1 |  
| 2 | 2 |  
| 2 | 3 |  
| 2 | 4 |  
| 4 | 2 |  
| 5 | 5 |  
| 5 | 6 |  
| 5 | 7 |  
| 5 | 8 |  
| 9 | 7 |  
| 6 | 2 |  
| 7 | 2 |  
| 6 | 3 |  
| 8 | 3 |  
| 12 | 6 |  
| 12 | 5 |  
| 10 | 3 |  
| 11 | 3 |  
| 10 | 4 |  
| 11 | 4 |  
| 17 | 8 |  
| 13 | 2 |  
| 14 | 2 |  
| 13 | 3 |  
| 15 | 3 |  
| 13 | 4 |  
| 16 | 4 |  
| 5 | 1 |  
±--------------±-------------+  
28 rows in set (0.00 sec)

=\> the last row has been inserted: User #1 now is in groups 1 and 5  
(from a database point of view).

However, u.core\_groups does not reflect this:

> > u.core\_groups  
> > =\> [#\<CoreGroup id: 1, parent\_id: nil, type: “”, name: “Administrators”,  
> > is\_open: false, is\_system\_group: true, rights: “”,  
> > serialized\_custom\_data: “BAh7AA==\n”, created\_at: “2008-02-10 20:25:44”,  
> > updated\_at: “2008-02-10 20:25:44”\>]

only when I restart the console, the change is seen:

> > u.core\_groups  
> > =\> [#\<CoreGroup id: 1, parent\_id: nil, type: “”, name: “Administrators”,  
> > is\_open: false, is\_system\_group: true, rights: “”,  
> > serialized\_custom\_data: “BAh7AA==\n”, created\_at: “2008-02-10 20:25:44”,  
> > updated\_at: “2008-02-10 20:25:44”\>, #\<CoreGroup id: 5, parent\_id: nil,  
> > type: “”, name: “Lecturers”, is\_open: false, is\_system\_group: true,  
> > rights: “”, serialized\_custom\_data: “BAh7AA==\n”, created\_at:  
> > “2008-02-10 20:25:44”, updated\_at: “2008-02-10 20:25:44”\>]

is this normal?

Please tell me if you need more information. thanks!

–  
Felix N.

---

<div class="post-metadata">

**Author:** ![Felix\_N](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/c57346/32.png) [@Felix\_N](https://www.ruby-forum.com/u/Felix_N)\
**Post date:** [February 10, 2008, 10:12pm UTC](https://www.ruby-forum.com/t/problem-with-has-and-belongs-to-many/128709/3 "2008-02-10T22:12:32Z")

</div>

On 10 Feb 2008, at 20:33, Felix N. wrote:

> 20:25:44", updated\_at: “2008-02-10 20:25:44”\>]  
> 20:25:44"\>]
> 
> is this normal?

AR is like that. if you set an association, rails does not magically  
update the other in memory object with the reverse association (but it  
is there in the database of course).

Fred

---

<div class="post-metadata">

**Author:** ![Felix\_N](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/c57346/32.png) [@Felix\_N](https://www.ruby-forum.com/u/Felix_N)\
**Post date:** [February 10, 2008, 10:30pm UTC](https://www.ruby-forum.com/t/problem-with-has-and-belongs-to-many/128709/4 "2008-02-10T22:30:49Z")

</div>

> AR is like that. if you set an association, rails does not magically  
> update the other in memory object with the reverse association (but it  
> is there in the database of course).

I don’t know if anyone mentioned to throw a ‘reload()’ call in  
somewhere…

–  
Phlip

---

<div class="post-metadata">

**Author:** ![Felix\_N](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/c57346/32.png) [@Felix\_N](https://www.ruby-forum.com/u/Felix_N)\
**Post date:** [February 10, 2008, 10:59pm UTC](https://www.ruby-forum.com/t/problem-with-has-and-belongs-to-many/128709/5 "2008-02-10T22:59:06Z")

</div>

“Phlip” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> > AR is like that. if you set an association, rails does not magically  
> > update the other in memory object with the reverse association (but it  
> > is there in the database of course).
> 
> I don’t know if anyone mentioned to throw a ‘reload()’ call in somewhere…

That was it, thanks to both of you! So I should change this code:

clas CoreGroup…  
def add(user)  
core\_users \<\< user if !core\_users.include?(user)  
end

def remove(user)  
core\_users.delete(user)  
end

to this:

class CoreGroup…  
def add(user)  
core\_users \<\< user if !core\_users.include?(user)  
user.save  
user.reload # make ActiveRecord update relation info  
end

def remove(user)  
core\_users.delete(user)  
user.save  
user.reload # make ActiveRecord update relation info  
end

? Or is there a method to just reload the relation info?

thanks in advance,

–  
Felix N.

---

<div class="post-metadata">

**Author:** ![Felix\_N](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/c57346/32.png) [@Felix\_N](https://www.ruby-forum.com/u/Felix_N)\
**Post date:** [February 11, 2008, 6:44pm UTC](https://www.ruby-forum.com/t/problem-with-has-and-belongs-to-many/128709/6 "2008-02-11T18:44:15Z")

</div>

Strictly speaking, I don’t believe that it’s necessary to call save on  
the user object. I realize that you’re probably doing this because  
the user object may contain unsaved data, but it brings a second  
purpose to your method.

Also, if you ever need to use the results of CoreGroup#add or  
CoreGroup#remove in subsequent processing then the results are going  
to be a little muddied. As coded you will always save/reload the user  
regardless of whether the association was added/deleted.  
Consequently, the method will always return the result of the  
user.reload call. You could get into confusing scenarios if the  
results conflicted (e.g., association creaated but reload failed,  
association failed but reload succeeded).

Maybe something like this:

def add(user)  
return false if core\_users.include?(user)  
if core\_users \<\< user  
user.core\_groups.reload  
return true  
end  
false  
end

For your primary purpose you just need to know that you can call  
‘reload’ on the association proxy to reload only that collection. The  
rest of the code only guarantees that you get a pass/fail response  
related to adding/removing on the collection.

On Feb 10, 4:59 pm, Felix N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![Felix\_N](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/c57346/32.png) [@Felix\_N](https://www.ruby-forum.com/u/Felix_N)\
**Post date:** [February 10, 2008, 11:06pm UTC](https://www.ruby-forum.com/t/problem-with-has-and-belongs-to-many/128709/7 "2008-02-10T23:06:09Z")

</div>

> ? Or is there a method to just reload the relation info?

I have seen this around:

user.core\_groups(:reload)

If it does what it says, then it only repopulates the controlled array  
inside core\_groups, without reloading the entire user object, or forcing  
a  
reload of everything else

Insert standard complaint here about how awesome ActiveRecord  
associations  
look just before you put a cycle in your code and discover the risk of  
dirty  
data bugs! → []

–  
Phlip  
[http://assertxpath.rubyforge.org/classes/Assert2\_0.html](http://assertxpath.rubyforge.org/classes/Assert2_0.html)
