Two similar Many-to-Many Relationships - ruby-on-rails

I have two similar M:M relationships that I work with individually however I don't know how to them working without conflicting.
Relationships are Players & Teams
1) Many players "play for" many teams
2) Many players "are members of" many teams
class Player < ActiveRecord::Base
has_many :plays
has_many :members
has_many :teams, through: :plays
has_many :teams, through: :members
end
class Teams < ActiveRecord::Base
has_many :plays
has_many :members
has_many :players, through: :plays
has_many :players, through: :members
end
class Play < ActiveRecord::Base
belongs_to :players
belongs_to :teams
end
class Member < ActiveRecord::Base
belongs_to :players
belongs_to :teams
end
I need to be able to find:
Player.find(21).teams #who he plays for
Player.find(21).teams #who he is a member of

You have to give a different name to each has_many association, and use the source parameter to specify the actual association name.
Like this:
class Player < ActiveRecord::Base
has_many :plays
has_many :memberships
has_many :played_with_teams, through: :plays, source: :team
has_many :member_of_teams, through: :memberships, source: :team
end
class Team < ActiveRecord::Base
has_many :plays
has_many :memberships
has_many :played_players, through: :plays, source: :player
has_many :member_players, through: :members, source: :player
end
class Play < ActiveRecord::Base
belongs_to :player
belongs_to :team
end
class Membership < ActiveRecord::Base
belongs_to :player
belongs_to :team
end
That can be used like this:
Player.find(21).played_with_teams #who he plays for
Player.find(21).member_of_teams #who he is a member of
Hint: I updated the answer.

Related

How to differentiate similar has_many :through associations in Rails?

I'll start off with my models:
class Project < ApplicationRecord
has_many :permissions
has_many :wallets, through: :permissions
has_many :follows
has_many :wallets, through: :follows
end
class Permission < ApplicationRecord
belongs_to :project
belongs_to :wallet
end
class Follow < ApplicationRecord
belongs_to :project
belongs_to :wallet
end
class Wallet < ApplicationRecord
has_many :permissions
has_many :projects, through: :permissions
has_many :follows
has_many :projects, through: :follows
end
As you can see, Permission and Follow are both through associations for Projects and Wallets.
They serve different purposes (Permission gives Wallets access to manage Projects while Follow lets Wallets "follow" projects for updates).
So how can I differentiate them? For example, if I do Wallet.find(1).projects, it defaults to using the "Follow" model...though in some scenarios I'd want it to actually use the "Permission" model.
Believe you'd find it will default to the has_many :projects that is defined last.
Need to give the associations different names, which will require something like ...
class Wallet < ApplicationRecord
has_many :permissions
has_many :projects, through: :permissions
has_many :follows
has_many :follow_projects, through: :follows, source: :project
end

Rails Complex Model Association, Shared Document Between Users and Teams

I have a complex model association in mind, and was wondering how I could accomplish it. This is what i want to accomplish.
I have a User and a Document model
A User can create documents. He is now the document admin.
He can add other users to his document, and give them permissions such as Editor, Viewer, Admin
He can also make a team, a group of users, and add multiple teams to his document. Each user on a team that the User has added to his document will also have permissions. A user can belong to many teams.
I am a little bit confused about the associations I will have to setup. This is the code I have so far, which has not incorporated the team aspect:
class User < ApplicationRecord
has_many :participations
has_many :documents, through: :participations
end
class Document < ApplicationRecord
has_many :participations
has_many :users, through: :participations
end
class Participation < ApplicationRecord
belongs_to :user
belongs_to :document
enum role: [ :admin, :editor, :viewer ]
end
I would recommend introducing a Team and TeamMembership models in a similary way to existing models. Also change the belongs_to association on Participation from user to a polymorphic participant.
class Team < ApplicationRecord
has_many :team_memberships
has_many :users, through: :team_memberships
has_many :participations, as: :participant
end
class TeamMembership < ApplicationRecord
belongs_to :team
belongs_to :user
end
class User < ApplicationRecord
has_many :team_memberships
has_many :teams, through: :team_memberships
has_many :participations, as: :participant
end
class Participation < ApplicationRecord
belongs_to :participant, polymorphic: true
belongs_to :document
enum role: [ :admin, :editor, :viewer ]
end
class Document < ApplicationRecord
has_many :participations
# if desired create a users method to conveniently get a list of users with access to the document
def users
#users ||= participations.flat_map do |participation|
case participation.partipant
when User
[participation.participant]
when Team
participation.participant.users
end
end
end
end
I would only add has_many :through associations as you discover a benefit/need to having them. That will reduce complexity of maintaining them unless you have specific use case for them. In the case of User having a teams association, it's pretty obvious that you'll be likely to want to get the teams that the user is a part of and since there's no specific information in the TeamMembership object that you are likely to need in that determination, it's a good has_many :through to have.
EDIT: Added Document model.
Since you already have a participation model, you can use that as the join model between users and teams. Since a user can belong to multiple teams, and a document can have multiple teams, you can use a has_many through relationship between teams and documents. We'll call it the DocumentTeam model.
class User < ApplicationRecord
has_many :participations
has_many :documents, through: :participations
has_many :teams, through: :participations
end
class Participation < ApplicationRecord
belongs_to :document
belongs_to :user
belongs_to :team, optional: true
enum role: [ :admin, :editor, :viewer ]
end
class Team < ApplicationRecord
has_many :participations
has_many :users, through: :participations
has_many :document_teams
has_many :document, through: :document_teams
end
class Document < ApplicationRecord
has_many :participations
has_many :users, through: :participations
has_many :document_teams
has_many :teams, through: :document_teams
end
class DocumentTeam < ApplicationRecord
belongs_to :document
belongs_to :team
end

How to setup model associations in Rails

I'm trying to create associations for three models in my Rails application. In the application a User can access courses which have videos. How would I model this?
This is what I currently have:
class User < ApplicationRecord
has_many :courses
has_many :videos, through: :courses
end
class Course < ApplicationRecord
belongs_to :user
has_many :videos
end
class Video < ApplicationRecord
belongs_to :course
belongs_to :user
end
Is this the correct way to model these associations for what I want the application to be able to achieve?
Normally, this would look something like:
class UserCourse < ApplicationRecord
belongs_to :user
belongs_to :course
end
class User < ApplicationRecord
has_many :user_courses
has_many :courses, through: :user_courses
has_many :videos, through: :courses
end
class Course < ApplicationRecord
has_many :user_courses
has_many :users, through: :user_courses
has_many :videos
end
class Video < ApplicationRecord
belongs_to :course
has_many :users, through: :course
end
That should let you do:
#user.courses
#user.videos
#course.users
#course.videos
#video.course
#video.users
(Assuming, of course, you've instantiated each of the above variables and you have associated records.)

How to correctly write my relationship/associations in Rails?

I have 4 Models and i am not sure what is the correct way to write my relationships/associations.
class User < ActiveRecord::Base
has_many :boards
has_many :lists
has_many :cards
end
class Board < ActiveRecord::Base
belongs_to :user
has_many :lists
end
class List < ActiveRecord::Base
belongs_to :user
belongs_to :board
has_many :cards
end
class Card < ActiveRecord::Base
belongs_to :user
belongs_to :list
end
If you want to be more explicit about your relationships, feel free to do the following (preferred in most every case):
class User < ActiveRecord::Base
has_many :boards, inverse_of: :user, dependent: :destroy
has_many :lists, inverse_of: :user, dependent: :destroy
has_many :cards, inverse_of: user, dependent: :destroy
end
class Board < ActiveRecord::Base
belongs_to :user, inverse_of: :boards
has_many :lists, inverse_of: :board
end
class List < ActiveRecord::Base
belongs_to :user, inverse_of: :lists
belongs_to :board, inverse_of :lists
has_many :cards, inverse_of :list
end
class Card < ActiveRecord::Base
belongs_to :user, inverse_of: :cards
belongs_to :list, inverse_of :cards
end
Finally, make sure any of your models that are dependent on another (e.g. Board belongs_to User) have the appropriate foreign key in their table. So, for example, Board will need to have a user_id foreign key to correctly create the association.
You can create a migration for any of those entities if you haven't already like so:
rails generate migration AddUserRefToBoards user:references

has_many of the same table through several others

What am trying to do is:
i have a User model and i have a Task model
Task has 2 types of users Owners and Supervisors all of them are users !
so what i have so far is:
Task Model
class Task < ActiveRecord::Base
has_many :task_owners, dependent: :destroy
has_many :task_supervisors, dependent: :destroy
has_many :users, through: :task_owners
has_many :users, through: :task_supervisors
end
TaskSupervisor Model
class TaskSupervisor < ActiveRecord::Base
belongs_to :task
belongs_to :user
end
TaskOwner Model
class TaskOwner < ActiveRecord::Base
belongs_to :task
belongs_to :user
end
and finally the User Model
class User < ActiveRecord::Base
has_many :task_owners
has_many :task_supervisors
has_many :tasks, through: :task_owners
has_many :tasks, through: :task_supervisors
end
now as you can imagine ... my problem is when i get a task and retrieve the users i only get one of my associations ... what i need is a way to change the getters name or identify them some how basically to be able to say something like
task.owners
task.supervisors
class Task < ActiveRecord::Base
has_many :task_owners, dependent: :destroy
has_many :task_supervisors, dependent: :destroy
has_many :owners, through: :task_owners, source: :users
has_many :supervisors, through: :task_supervisors, source: :users
end
You should be able to do this.
Then you should get your task.owners and task.supervisors
Edit:
You will need to change your user model to
class User < ActiveRecord::Base
has_many :task_owners
has_many :task_supervisors
has_many :owned_tasks, through: :task_owners, source: :tasks
has_many :supervised_tasks, through: :task_supervisors, source: :tasks
end

Resources