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