How do you find relationships using has_and_belongs_to_many?

I’m trying to list out the tasks that belongs to the logged-in user’s
groups.

For instance, I have the models:

Task - belongs_to :group
Group - has_and_belongs_to_many :users

My User model has both:
has_many :tasks
has_and_belongs_to_many :groups

If I wanted to list the tasks that belongs to the user’s groups, how
would I do it?

I tried current_user.groups.tasks, but I’m getting no success.

Any ideas?

Bob S. wrote:

I’m trying to list out the tasks that belongs to the logged-in user’s
groups.

For instance, I have the models:

Task - belongs_to :group
Group - has_and_belongs_to_many :users

My User model has both:
has_many :tasks
has_and_belongs_to_many :groups

If I wanted to list the tasks that belongs to the user’s groups, how
would I do it?

I tried current_user.groups.tasks, but I’m getting no success.

Any ideas?

you must select the group you want the tasks for

current_user.groups is an array in this case, so you can do something
like
current_user.groups[0].tasks

or use a find:
current_user.groups.find_by_id(your_id).tasks

maybe in User:

has_many :tasks, :through => :groups

should do what you want (not sure, it works exactly that way, but
should)
then you should be able to access

@current_user.tasks

Post < Model
has_and_belongs_to_many :tags

Tag < Model

none

Then u needs a table whiteout id.
posts_tags with column post_id and tag_id

That’s it

/Frank Vilhelmsen

2007/11/14, Bob S. [email protected]:

For example, let’s say a user belongs to two groups: “Programmers”,
“Designers”. Now, I would like to display the tasks that belong to both
“Programmers” and “Designers”.

Would you know a good way to do that?

hm, seems i didn’t read it carefully enough :slight_smile:

ok, another try, a bit more complicated, since you must write your own
finder method since :through doesn’t work with habtm

:has_and_belongs_to_many :groups do
def all_tasks

here goes code to find the tasks

something like (only sketched it out)

my_ids = groups.map{|group| group.id}
Task.find(:all, "SELECT ALL FROM 'tasks' WHERE id 

IN(#{}my_ids.join(’,’))"
end
end

but that would give you mainly a nice syntax, the finder would have to
do most work with find_by_sql, getting the id’s of the related groups
and search tasks for them
sorry, not absolutely nice, but the best i can think about right now

Hi Thorston,

Thanks for your help. I appreciate it!

What if I wanted to find all tasks that belong to ALL of the user’s
groups?

For example, let’s say a user belongs to two groups: “Programmers”,
“Designers”. Now, I would like to display the tasks that belong to both
“Programmers” and “Designers”.

Would you know a good way to do that?

Thorsten M. wrote:

For example, let’s say a user belongs to two groups: “Programmers”,
“Designers”. Now, I would like to display the tasks that belong to both
“Programmers” and “Designers”.

Would you know a good way to do that?

hm, seems i didn’t read it carefully enough :slight_smile:

ok, another try, a bit more complicated, since you must write your own
finder method since :through doesn’t work with habtm

:has_and_belongs_to_many :groups do
def all_tasks

here goes code to find the tasks

something like (only sketched it out)

my_ids = groups.map{|group| group.id}
Task.find(:all, "SELECT ALL FROM 'tasks' WHERE id 

IN(#{}my_ids.join(’,’))"
end
end

but that would give you mainly a nice syntax, the finder would have to
do most work with find_by_sql, getting the id’s of the related groups
and search tasks for them
sorry, not absolutely nice, but the best i can think about right now

Thanks, Thorsten. That looks way over my head. I tried it, but I seem to
be too much of a beginner to implement it correctly.

To make it simpler, should I use use :through associations instead?

By the way Thorsten, I really appreciate your help on this :). It’s
awesome to
find such a helpful community.

To make it simpler, should I use use :through associations instead?

would not really help, because the problems would stay the same and you
would have to organize your own relation table/model, which in turn
would have to provide the same functionality (many to many is a bit of
work)

by the way i just saw some errors:
so the whole thing:

:has_and_belongs_to_many :groups do
def all_tasks
Task.find_by_sql(“SELECT * FROM tasks WHERE id
IN(#{groups.map{|group| group.id}.join(’,’)})”)
end
end

then you should get your tasks with

current_user.groups.all_tasks

give it a try, just a minute of copy and paste, (if i didn’t make a mess
outa those braces again, but this time i hacked it in script/console, so
at least the syntax is ok) :slight_smile:

You could do this. I’m sure there is a better way but if you use
eager loading when pulling the current_user object from the database,
the overhead shouldn’t be bad.

@tasks = []
current_user.groups.each do |group|
@tasks << group.tasks
end

On Nov 14, 8:22 am, Thorsten M. [email protected]

:has_and_belongs_to_many :groups do
def all_tasks
Task.find_by_sql(“SELECT * FROM tasks WHERE id
IN(#{groups.map{|group| group.id}.join(’,’)})”)
end
end

should be:
:has_and_belongs_to_many :groups do
def all_tasks
Task.find_by_sql(“SELECT * FROM tasks WHERE group_id
IN(#{groups.map{|group| group.id}.join(’,’)})”)
end
end

Bob S. wrote:

On Nov 14, 2007 5:55 AM, Bob S. [email protected]
wrote:

has_many :tasks
has_and_belongs_to_many :groups

If I wanted to list the tasks that belongs to the user’s groups, how
would I do it?

I tried current_user.groups.tasks, but I’m getting no success.

(“no success” is too vague. You need to show the specific results or
error messages you’re getting. Otherwise, we need to break out the
crystal ball.)

Assuming you have the tables set up correctly (you need a groups_users
join table), your problem is that current_user.groups doesn’t have a
tasks method. It’s a collection of Group rows, each of which would
have a tasks method.

You need to iterate over the groups:

all_tasks = current_user.groups.collect(&:tasks).flatten

HTH

Thanks for everyone’s help! I grateful. Thank you.

Bob S.'s one-liner solution
(current_user.groups.collect(&:tasks).flatten) worked perfectly. That
was exactly what I needed. Thanks a bunch, Bob – and my apologies for
not being clearer.

You all are wonderfully helpful. Thank you!

On Nov 14, 2007 5:55 AM, Bob S. [email protected]
wrote:

has_many :tasks
has_and_belongs_to_many :groups

If I wanted to list the tasks that belongs to the user’s groups, how
would I do it?

I tried current_user.groups.tasks, but I’m getting no success.

(“no success” is too vague. You need to show the specific results or
error messages you’re getting. Otherwise, we need to break out the
crystal ball.)

Assuming you have the tables set up correctly (you need a groups_users
join table), your problem is that current_user.groups doesn’t have a
tasks method. It’s a collection of Group rows, each of which would
have a tasks method.

You need to iterate over the groups:

all_tasks = current_user.groups.collect(&:tasks).flatten

HTH

Bob S. wrote:

On Nov 14, 2007 5:55 AM, Bob S. [email protected]
wrote:

has_many :tasks
has_and_belongs_to_many :groups

If I wanted to list the tasks that belongs to the user’s groups, how
would I do it?

I tried current_user.groups.tasks, but I’m getting no success.

(“no success” is too vague. You need to show the specific results or
error messages you’re getting. Otherwise, we need to break out the
crystal ball.)

Assuming you have the tables set up correctly (you need a groups_users
join table), your problem is that current_user.groups doesn’t have a
tasks method. It’s a collection of Group rows, each of which would
have a tasks method.

You need to iterate over the groups:

all_tasks = current_user.groups.collect(&:tasks).flatten

HTH

One more noob question: I’m trying to use your array to filter out only
tasks that are marked “finished”. I have a column called “finished” for
each task. How would I do for your array
current_user.groups.collect(&:tasks).flatten?

On Nov 14, 2007 5:32 PM, Bob S. [email protected]
wrote:

One more noob question: I’m trying to use your array to filter out only
tasks that are marked “finished”. I have a column called “finished” for
each task. How would I do for your array
current_user.groups.collect(&:tasks).flatten?

Well, the easy way is (I’m assuming you have a boolean column called
“finished”):

current_user.groups.collect(&:tasks).flatten.collect(&:finished?)

But that’s not very efficient, since it retrieves all tasks and then
throws away the unfinished ones.

You could call find() on the tasks collection proxy for an improvement:

current_user.groups.collect {|g|
g.tasks.find_by_finished(true)}.flatten

It’s probably even more efficient to start from the Task model.
Something like this:

Task.find :all,
:include => :groups,
:conditions => [‘tasks.finished=1 and groups.user_id=?’,
current_user.id]

Bob S. wrote:

On Nov 14, 2007 5:32 PM, Bob S. [email protected]
wrote:

One more noob question: I’m trying to use your array to filter out only
tasks that are marked “finished”. I have a column called “finished” for
each task. How would I do for your array
current_user.groups.collect(&:tasks).flatten?

Well, the easy way is (I’m assuming you have a boolean column called
“finished”):

current_user.groups.collect(&:tasks).flatten.collect(&:finished?)

But that’s not very efficient, since it retrieves all tasks and then
throws away the unfinished ones.

You could call find() on the tasks collection proxy for an improvement:

current_user.groups.collect {|g|
g.tasks.find_by_finished(true)}.flatten

It’s probably even more efficient to start from the Task model.
Something like this:

Task.find :all,
:include => :groups,
:conditions => [‘tasks.finished=1 and groups.user_id=?’,
current_user.id]

That’s perfect, Bob :slight_smile:

Thank you so much!

Erik, I’ll definitely check out the plugin. Thanks for the heads-up.

I think you would benefit from looking at the scope_out plugin. It is
excellent for tasks like these.

Regards

Erik L.