Home > Mobile >  how can create relation with has many through in rails
how can create relation with has many through in rails

Time:09-16

I have 3 model User Project Bug. I want to create many to many relation with through. I create the relation in model i don't know it is correct or not, user have user type column which is enum type user type contain developer, manager , QA

user.user_type.manager belong to many project it has one to many relation 
user.user_type.developer has many project and many project belong to developer. it has many to many realtion 

project has many bugs and bugs belong to project 

developer has many bugs and many bugs belong to developer

bug model

class Bug < ApplicationRecord
   belongs_to :project
   has_many :developers, -> { where user_type: :Developer }, class_name: 'User', through: :project, source: :bugs

end

project model

class Project < ApplicationRecord
   has_many :bugs, dependent: :delete_all
   has_many :developers, -> { where user_type: :Developer }, class_name: 'User', through: :users, source: :project
   has_many :users  //it belong to manager_id
end

user model

class User < ApplicationRecord

   enum user_type: %i[Manager Developer QA]  
   has_many :projects
   has_many :bugs
end

developer_bug model

class DevelopersBug < ApplicationRecord
  has_many :bugs
  has_many :users
end

project_developer model

class ProjectsDeveloper < ApplicationRecord
  has_many :projects
  has_many :users
end

CodePudding user response:

This

has_many :developers, -> { where user_type: :Developer },
         class_name: 'User',
         through: :users,
         source: :project

is not what you think it is. It means something on the line of:

I already have an association 'users'. The users have an association 'project'.

Please configure an association that makes both JOINs and gives me the list of projects associated to the associated users.

This association will be named "developers" and be of objects of class "User".

You can see how these instructions are inconsistent. This

has_many :projects, through: :users, source: :project

will define a list of associated projects, by jumping over users.

On the other side, this:

has_many :developers, -> { where user_type: :Developer }, class_name: 'User'

will define a direct has-many association with a subset of all the users.

Given your description, your data model seems wrong, maybe this will be better:

class User < ApplicationRecord
  has_many :managed_projects, inverse_of: :manager, class_name: 'Project'
  has_and_belongs_to_many :projects
  has_many :bugs
end

class Project < ApplicationRecord
  belongs_to :manager, class_name: 'User', inverse_of: :managed_projects
  has_and_belongs_to_many :users
  has_many :bugs
end

class Bug < ApplicationRecord
  belongs_to :user
  belongs_to :project
end

Your schema should include the three tables, and an additional many-to-many join table projects_users that holds foreign keys to both users and projects.

CodePudding user response:

As rewritten has already pointed in his excellent answer out your data model is flawed. What you want instead is a join table which joins the users and projects:

class User < ApplicationRecord
  has_many :project_roles
  has_many :projects, through: :project_roles
end

class ProjectRole < ApplicationRecord
  belongs_to :user
  belongs_to :project
end

class Project < ApplicationRecord
  has_many :users
  has_many :projects, through: :project_roles
end

If you then want to give the user specific roles in a project you would add the enum to the join table and this is where it starts to get hairy so bear with me here:

class ProjectRole < ApplicationRecord
  enum roles: [:manager, :developer, :qa]
  belongs_to :user
  belongs_to :project
end
class User < ApplicationRecord
  has_many :project_roles
  has_many :projects, through: :project_roles

  has_many :project_roles_as_manager,
    -> { manager }, # short for `where(role: :manager)` 
    class_name: 'ProjectRole'
    
  has_many :projects_as_manager,
    class_name: 'Project',
    through: :project_roles_as_manager,
    source: :project

  has_many :project_roles_as_developer,
    -> { developer },
    class_name: 'ProjectRole'
    
  has_many :projects_as_developer,
    class_name: 'Project',
    through: :project_roles_as_developer,
    source: :project
  
  # ...
end

This defines associations with a default scope and then joins through that association. You would then do the same thing on the other end of the assocation:

class Project < ApplicationRecord
  has_many :users
  has_many :projects, through: :project_roles
  has_many :manager_project_roles,
    -> { manager }, 
    class_name: 'ProjectRole'
  has_many :managers,
    through: :manager_project_roles,
    source: :user

  # ...
end

Of course this is a lot of duplication which you can cut by looping over ProjectRoles.roles.keys and defining the assocations dynamically.

This is a very flexible way of modeling it which makes as few assumptions about the domain as possible. For example it allows multiple managers for a project and it allows users to have different roles in different projects.

If you want to model "bugs" as you would typically would with issues in a tracker you would create one table for the bug and a join table for the assignment:

class Bug < ApplicationRecord
  belongs_to :project
  has_many :bug_assignments
  has_many :users, through: :bug_assignments
end
class BugAssignment < ApplicationRecord
  has_one :project, through: :bug
  belongs_to :bug
  belongs_to :user
end
class User < ApplicationRecord
  # ... 
  has_many :bug_assignments
  has_many :bugs
end
  • Related