Model relationship question

I'm a newbie in the programming world and I need some advice regarding
the best way of achieving the task I'm gonna describe.

I've got an User model and another model called Sandbox.

Now I want to make each sandbox have an "owner" (administrator), who
is also an user. The user will be able to own several sandboxes.

Than I want sandboxes to also have "participants" (who are Users, from
the User model/table, but without admin status). A Sandbox should be
able to have many participants and a User should be able to
participate in many sandboxes.

Any idea of how to approach this?

Daniel Drehmer wrote:

I'm a newbie in the programming world and I need some advice regarding
the best way of achieving the task I'm gonna describe.

I've got an User model and another model called Sandbox.

Now I want to make each sandbox have an "owner" (administrator), who
is also an user. The user will be able to own several sandboxes.

Than I want sandboxes to also have "participants" (who are Users, from
the User model/table, but without admin status). A Sandbox should be
able to have many participants and a User should be able to
participate in many sandboxes.

Any idea of how to approach this?

Do a little research on "belongs_to", "has_many",
"has_and_belongs_to_many" (also known as HABTM), as well as primary /
foreign keys in databases.
It should give you, hopefully, the insight to go on.

If you haven't seen them already have a look at the Rails Guides.
Particularly Getting Started and ActiveRecord Associations.

Colin

Any idea of how to approach this?

Sounds like Users, Sandboxes, and Participations - the main point being
a full join table (participation) with additional attributes

User
has_many :participations
has_many :sandboxes, :through => :participations

Sandbox
has_many :participations
has_many :users, :through => :participations

Participation
user_id
sandbox_id
role (which is user, or admin, or donkey, or whatever)
belongs_to :user
belongs_to :sandbox

Or something along those lines...

Thank you Ar Chron! This is exactly what I was looking for.

I appreciate the other answers, but those reading this thread should
notice that the case I described could not be solved without this
"role" attribute in the participation model.

This was the missing piece. Thank you!

The only thing that remains unknown to me is how query the list of
users of a given sandbox that have the role "donkey". Is it possible
to use the find method the rails way, without "injecting" sql?

The only thing that remains unknown to me is how query the list of
users of a given sandbox that have the role "donkey". Is it possible
to use the find method the rails way, without "injecting" sql?

Yes of course, you should very rarely find yourself using sql. If you
have a Sandbox called @sandbox then the users of that sandbox are
@sandbox.users so those that have the role donkey are
@sandbox.users.find_by_role('donkey')
At least I think you can do that, I prefer to use a named scope rather
than find_by.. So you might have a named scope on User called
by_role(role) that returns the Users with the given role. Then you
would use
@sandbox.users.by_role('donkey')
Or if you often wanted to find the donkeys then you might have a named
scope on User called donkies that finds them and then you could say
@sandbox.users.donkies

Colin

Also very elucidative, Colin. Thank you very much!

I am not sure I have ever been accused of being that before.

Colin

@sandbox.users.find_by_role('donkey')

Well, it turns out you can't do it like that. But I'm looking into
named scopes, to see if it solves my problem.

I'm sure there's a better way (I haven't fiddled with named_scope before
this evening) but:

class Participation < ActiveRecord::Base
  belongs_to :user
  belongs_to :sandbox
  named_scope :who_are_admins, :conditions => ['role = ?', 'admin']
  named_scope :who_are_users, :conditions => ['role = ?', 'user']
  named_scope :who_are_donkeys, :conditions => ['role = ?', 'donkey']
end

class Sandbox < ActiveRecord::Base
  validates_presence_of :name
  has_many :participations
  has_many :users, :through => :participations
  # just to see what the show syntax would look like
  def donkeys
    self.participations.who_are_donkeys
  end
  def admins
    self.participations.who_are_admins
  end
end

And the show.html.erb

<p>
  <b>Name:</b>
  <%=h @sandbox.name %>
</p>
<p><b>Users (:through)</b><br/>
<% @sandbox.users.each do |user| %>
<%=h user.first+' '+user.last %><br/>
<% end %>
</p>
<p><b>Admins:</b><br/>
<% @sandbox.participations.who_are_admins.each do |admin| %>
<%=h admin.user.first+' '+admin.user.last %><br/>
<% end %>
</p>
<p><b>Users:</b><br/>
<% @sandbox.participations.who_are_users.each do |user| %>
<%=h user.user.first+' '+user.user.last %><br/>
<% end %>
</p>
<p><b>Donkeys:</b><br/>
<% @sandbox.participations.who_are_donkeys.each do |donkey| %>
<%=h donkey.user.first+' '+donkey.user.last %><br/>
<% end %>
</p>
<p><b>Donkeys (sandbox method form):</b><br/>
<% @sandbox.donkeys.each do |donkey| %>
<%=h donkey.user.first+' '+donkey.user.last %><br/>
<% end %>
</p>

I'm sure there's a better way (I haven't fiddled with named_scope before
this evening) but:

class Participation < ActiveRecord::Base
belongs_to :user
belongs_to :sandbox
named_scope :who_are_admins, :conditions => ['role = ?', 'admin']
named_scope :who_are_users, :conditions => ['role = ?', 'user']
named_scope :who_are_donkeys, :conditions => ['role = ?', 'donkey']

If you want a parameterised named_scope you can use
named_scope :by_role, lambda { |role| { :conditions => ['role = ?', role] }
then you can say participations.by_role( 'donkey' )

Colin

end

class Sandbox < ActiveRecord::Base
validates_presence_of :name
has_many :participations
has_many :users, :through => :participations
# just to see what the show syntax would look like
def donkeys
self.participations.who_are_donkeys
end
def admins
self.participations.who_are_admins
end
end

And the show.html.erb

<p>
<b>Name:</b>
<%=h @sandbox.name %>
</p>
<p><b>Users (:through)</b><br/>
<% @sandbox.users.each do |user| %>
<%=h user.first+' '+user.last %><br/>
<% end %>
</p>
<p><b>Admins:</b><br/>
<% @sandbox.participations.who_are_admins.each do |admin| %>
<%=h admin.user.first+' '+admin.user.last %><br/>
<% end %>
</p>
<p><b>Users:</b><br/>
<% @sandbox.participations.who_are_users.each do |user| %>
<%=h user.user.first+' '+user.user.last %><br/>
<% end %>
</p>
<p><b>Donkeys:</b><br/>
<% @sandbox.participations.who_are_donkeys.each do |donkey| %>
<%=h donkey.user.first+' '+donkey.user.last %><br/>
<% end %>
</p>
<p><b>Donkeys (sandbox method form):</b><br/>
<% @sandbox.donkeys.each do |donkey| %>
<%=h donkey.user.first+' '+donkey.user.last %><br/>
<% end %>
</p>

Colin

@sandbox.users.find_by_role('donkey')

Well, it turns out you can't do it like that. But I'm looking into
named scopes, to see if it solves my problem.

I realise looking at your associations again that I should have said
@sandbox.participations.find_by_role('donkey')
I was thinking that it was the user that had the role rather than the
pariticipation. Does this not work either?

Well the quirky part is that yes, the user has a role, but only in the
context of a sandbox.

The admin for this one, a user in that one, hopefully a donkey in
none...

The show does work, but I admit I don't particularly like the syntax.
admins, users or donkeys are participations, not users directly.

If there are (and will always be) only the two categories of
participation, ie sandbox owner and regular participant then this
might be better

Sandbox
belongs_to :owner, :class_name => 'User', :foreign_key => owner_id
has_and_belongs_to_many :users

User
has_many :owned_sandboxes, :class_name => 'Sandbox', :foreign_key => owner_id
has_and_belongs_to_many :sandboxes

Or along those lines anyway, I may not have got the syntax quite right

This allows one to say
sandbox.owner the user that is the owner
sandbox.users all the other users
user.owned_sandboxes the ones (if any) that he owns
user.sandboxes all the rest that he participates in

Colin