如何在rails中有很多通过创建关系

Posted

技术标签:

【中文标题】如何在rails中有很多通过创建关系【英文标题】:how can create relation with has many through in rails 【发布时间】:2021-11-11 06:49:15 【问题描述】:

我有 3 个模型用户项目错误。我想通过创建多对多关系。我在模型中创建关系我不知道它是否正确,用户有用户类型列,它是枚举类型用户类型包含开发人员、经理、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

错误模型

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

end

项目模型

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

用户模型

class User < ApplicationRecord

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

developer_bug 模型

class DevelopersBug < ApplicationRecord
  has_many :bugs
  has_many :users
end

project_developer 模型

class ProjectsDeveloper < ApplicationRecord
  has_many :projects
  has_many :users
end

【问题讨论】:

使用snake_case 作为您的枚举键。 %i[manager developer qa] ActiveRecord::Enum 基于键生成方法,因此您将获得 User.Developer,这是非常非常规的并且会导致错误。例如,如果您在没有显式使用self 的情况下调用该方法,解析器将认为它是一个常量。 github.com/rubocop/… 【参考方案1】:

这个

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

不是你想的那样。它的意思是:

我已经有一个关联“用户”。用户有一个关联“项目”。

请配置一个关联,使两个 JOIN 都加入,并为我提供与关联用户关联的项目列表。

这个关联将被命名为“开发者”并且属于“用户”类的对象。

您可以看到这些说明是如何不一致的。这个

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

将通过跳过用户来定义相关项目的列表。

另一方面,这个:

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

将与所有用户的子集定义一个直接 has-many 关联。

根据您的描述,您的数据模型似乎错误,也许这样会更好:

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

您的架构应包括三个表,以及一个额外的多对多连接表 projects_users,其中包含用户和项目的外键。

【讨论】:

但我想将错误和开发人员的 ID 存储到单独的表中。我该怎么做呢 您能否改写一下,以便我了解您需要实现的目标?在我的建议中,bug 的 id 存储在 bugs 表中,开发人员的 id 存储在 users 表中。【参考方案2】:

正如重写已经在他的出色回答中指出的那样,您的数据模型存在缺陷。你想要的是一个连接用户和项目的连接表:

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

如果您想在项目中为用户提供特定角色,您可以将枚举添加到连接表中,这就是它开始变得棘手的地方,所以请耐心等待:

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

这定义了具有默认范围的关联,然后通过该关联加入。然后你会在关联的另一端做同样的事情:

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

当然,这是很多重复,您可以通过循环 ProjectRoles.roles.keys 并动态定义关联来减少这些重复。

这是一种非常灵活的建模方式,它对领域的假设尽可能少。例如,它允许一个项目有多个经理,它允许用户在不同的项目中拥有不同的角色。

如果您想像通常在跟踪器中处理问题那样对“错误”建模,您可以为错误创建一个表并为分配创建一个连接表:

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

【讨论】:

不错的方法,但我觉得它对 OP 来说有点太复杂了 :) @rewritten 我同意它有点太复杂了。但它是一种在与现实接触中幸存下来的模型。

以上是关于如何在rails中有很多通过创建关系的主要内容,如果未能解决你的问题,请参考以下文章

在 ruby​​ on rails 中以一对多关系创建新记录

Rails 与 Redshift - 我如何创建关系?

has_many的Rails组合框问题:通过关系

如何在 Rails 中为多对多关系(和中间表)做一个选择字段?

如何在rails中获得重复的月份和年份匹配记录?

如何在 Rails 应用程序中编辑 .vtt 轨道的提示文本