Active Record Associations: has_and_belongs_to_many, has_many :through or polymorphic association?

Active Record Associations: has_and_belongs_to_many, has_many :through or polymorphic association?

我正在开发的 Rails 应用程序上的 Ruby 允许 users 创建并与其他 users 共享 agendas

此外,我们必须能够:

我打算在 useragenda 模型之间建立 has_and_belongs_to_many 关联,例如:

class User < ActiveRecord::Base
  has_and_belongs_to_many :agendas
end

class Agenda < ActiveRecord::Base
  has_and_belongs_to_many :users
end

但后来我想知道这是否能让我在给定用户的给定议程页面上获取并显示 roles@user.agenda.user.role 列表。

而且我认为我应该改用 has_many :through 关联,例如:

class User < ActiveRecord::Base
  has_many :roles
  has_many :agendas, through: :roles
end

class Role < ActiveRecord::Base
  belongs_to :user
  belongs_to :agenda
end

class Agenda < ActiveRecord::Base
  has_many :roles
  has_many :users, through: :roles
end

虽然我对 user 有几个 roles(每个 agenda 一个)的想法很满意,但我不确定 agenda有几个roles(每个user?)。

最后,更让人困惑的是,我阅读了有关多态关联的内容,认为它也可能是一个可行的解决方案,例如,如果这样做的话:

class Role < ActiveRecord::Base
  belongs_to :definition, polymorphic: true
end

class User < ActiveRecord::Base
  has_many :roles, as: :definition
end

class Agenda < ActiveRecord::Base
  has_many :roles, as: :definition
end

以上任何解决方案听起来是否适合这种情况?

更新:在做一些研究时,我偶然发现这篇文章(从 2012 年开始)解释说 has_many :throughhas_and_belongs_to_many 更 "smarter" ].就我而言,我仍然不确定 agenda 会有很多 roles.

更新 2:正如@engineersmnkyn 在评论中所建议的那样,解决此问题的一种方法是使用两个连接表。我尝试实现以下代码:

class User < ActiveRecord::Base
  has_many :agendas, through: :jointable
end

class Agenda < ActiveRecord::Base

end

class Role < ActiveRecord::Base

end

class Jointable < ActiveRecord::Base
  belongs_to :user
  belongs_to :agenda
  has_many :agendaroles through :jointable2
end

class Jointable2 < ActiveRecord::Base
  belongs_to :roles
  belongs_to :useragenda
end

虽然我不确定语法。我在正确的轨道上吗?我应该如何定义 AgendaRole 模型?

更新 3:如果我使用类似的东西会怎样:

class User < ActiveRecord::Base
      has_many :roles
      has_many :agendas, through: :roles
    end

    class Role < ActiveRecord::Base
      belongs_to :user
      belongs_to :agenda
    end

    class Agenda < ActiveRecord::Base
      has_many :roles
      has_many :users, through: :roles
    end

然后,在迁移文件中,使用如下内容:

class CreateRoles < ActiveRecord::Migration
  def change
    create_table :roles do |t|
      t.belongs_to :user, index: true 
      t.belongs_to :agenda, index: true
      t.string :privilege
      t.timestamps
    end
  end
end

我能否调用@user.agenda.privilege 来获得给定用户针对给定议程的特权("role" 创建者、编辑者或查看者)?

相反,我可以调用@agenda.user.privilege吗?

好吧,我先说我没有测试过这个,但我认为这两个选择之一应该适合你。

此外,如果这些联接 table 永远不需要关系以外的功能,那么 has_and_belongs_to_many 会很好并且更简洁。

基本Rails经验法则:

If you need to work with the relationship model as its own entity, use has_many :through. Use has_and_belongs_to_many when working with legacy schemas or when you never work directly with the relationship itself.

首先使用您的示例 (http://repl.it/tNS):

class User < ActiveRecord::Base
  has_many :user_agendas
  has_many :agendas, through: :user_agendas
  has_many :user_agenda_roles, through: :user_agendas
  has_many :roles, through: :user_agenda_roles

  def agenda_roles(agenda)
    roles.where(user_agenda_roles:{agenda:agenda})
  end
end

class Agenda < ActiveRecord::Base
    has_many :user_agendas
    has_many :users, through: :user_agendas
    has_many :user_agenda_roles, through: :user_agendas
    has_many :roles, through: :user_agenda_roles

    def user_roles(user)
      roles.where(user_agenda_roles:{user: user})
    end
end

class Role < ActiveRecord::Base
  has_many :user_agenda_roles
end

class UserAgenda < ActiveRecord::Base
  belongs_to :user
  belongs_to :agenda
  has_many   :user_agenda_roles
  has_many   :roles, through: :user_agenda_roles 
end

class UserAgendaRoles < ActiveRecord::Base
  belongs_to :role
  belongs_to :user_agenda
end

这使用连接 table 来保存 User <=> Agenda 的关系,然后使用 table 来连接 UserAgenda => Role。

第二个选项是使用连接 table 来保存 User <=> Agenda 的关系,另一个连接 table 来处理 User <=> Agenda <=> Role 的关系.从 CRUD 的角度来看,此选项需要进行更多设置,例如验证用户是否是该议程的用户,但允许一点灵活性。

class User < ActiveRecord::Base
  has_many :user_agendas
  has_many :agendas, through: :user_agendas
  has_many :user_agenda_roles
  has_many :roles, through: :user_agenda_roles

  def agenda_roles(agenda)
      roles.where(user_agenda_roles:{agenda: agenda})
  end

end

class Agenda < ActiveRecord::Base
    has_many :user_agendas
    has_many :users, through: :user_agendas
    has_many :user_agenda_roles
    has_many :roles, through: :user_agenda_roles

    def user_roles(user)
        roles.where(user_agenda_roles:{user: user})
    end
end

class Role < ActiveRecord::Base
  has_many :user_agenda_roles
end

class UserAgenda < ActiveRecord::Base
  belongs_to :user
  belongs_to :agenda
end

class UserAgendaRoles < ActiveRecord::Base
  belongs_to :role
  belongs_to :user
  belongs_to :agenda
end

我知道这是一个很长的答案,但我想向您展示不止一种方法来解决这个问题。希望对你有帮助