我有3个命名空间用户类 - Users::SuperAdminUser
,Users::AdminUser
和Users::StandardUser
以及当前2个命名空间用户组类 - UserGroups::Team
和UserGroups::Projects
。我想在两个方向上创建多对多的多态关系(例如,管理员用户可以属于多个团队和多个项目,团队可以包含多个标准用户,多个超级管理员用户等)。
我已经尝试了一些方法,例如调整和混合这些解决方案(所有这些方法只在一个方向上是多态的),但我最终迷失在杂草中:
many to many polymorphic association
Rails: has_many through not returning correctly with namespaced models
http://www.informit.com/articles/article.aspx?p=2220311&seqNum=6
我主要使用了最后一个链接的方法。我试图通过添加一个表示用户类的“可列表”关注来向另一个方向建立关联多态性,作为现有“Memberable”关注点的补充,它表示用户组类(团队和项目,在我的情况下),但是没有用。
目前,我的用户模型如下所示:
module Users
class SuperAdminUser < ApplicationRecord
self.table_name = 'users_super_admin_users'
include UserGroups::Enlistable
has_many :user_groups_memberships
has_many :user_groups_teams, through: :memberships, source: :memberable, source_type: 'UserGroups::Team'
has_many :user_groups_projects, through: :memberships, source: :memberable, source_type: 'UserGroups::Project'
end
end
团队/项目类看起来像这样:
module UserGroups
class Team < ApplicationRecord
self.table_name = 'user_groups_teams'
include Memberable
has_many :user_groups_memberships
has_many :users_super_admin_users, through: :memberships, source: :enlistable, source_type: 'Users::SuperAdminUser'
has_many :users_admin_users, through: :memberships, source: :enlistable, source_type: 'Users::AdminUser'
has_many :users_standard_users, through: :memberships, source: :enlistable, source_type: 'Users::StandardUser'
end
end
会员类,如下所示:
module UserGroups
class Membership < ApplicationRecord
self.table_name = 'user_groups_memberships'
belongs_to :enlistable, polymorphic: true
belongs_to :memberable, polymorphic: true
end
end
可登记/可元关注的问题如下:
module UserGroups
module Enlistable
extend ActiveSupport::Concern
included do
has_many :memberships, as: :enlistable, dependent: :destroy
has_many :memberables, through: :memberships
end
end
end
以及如下所示的成员资格迁移:
class CreateUserGroupsMembership < ActiveRecord::Migration[5.0]
def change
create_table :user_groups_memberships do |t|
t.string :enlistable_type
t.integer :enlistable_id
t.string :memberable_type
t.integer :memberable_id
t.references :enlistable, polymorphic: true, index: { name: 'index_user_groups_memberships_on_enlistable_type_and_id' }
t.references :memberable, polymorphic: true, index: { name: 'index_user_groups_memberships_on_memberable_type_and_id' }
t.timestamps null: false
end
end
end
一切都很好地迁移,我使用每个用户类的多个以及团队和项目类为数据库播种。但我从那里难倒。如果我有一个特定的用户,让我们说Users::SuperAdminUser.first
,我想创建并访问它的关联,即将其分配给一个团队并列出他们所属的团队。如何设置它,并且一旦设置,我将如何执行这些操作?其中一个关键是能够在考虑命名空间的情况下完成此任务。
提前感谢您提供的任何指导。
答案 0 :(得分:0)
看来我有一个解决方案,一个与命名空间一起工作的解决方案,并且足够灵活,您可以在应用程序中的任意两组命名空间类之间创建双向,多对多,多态关联。它会自动生成反向关联,删除模型删除时涉及模型的任何关联,并确保没有重复的关联记录。我不知道它是多么优化,或者它是否能很好地扩展,但它似乎完全符合我的需要。不过,对于任何预见到的问题或潜在的陷阱,我都很感激。
<强>〜/应用/模型/用户/ super_admin_user.rb 强>
module Users
class SuperAdminUser < ApplicationRecord
include Polymorpher
self.table_name = 'users_super_admin_users'
Dir[Rails.root + 'app/models/user_groups/*.rb'].map { |f| File.path(f).to_s.sub(Rails.root.to_s + '/app/models/', '').chomp('.rb').camelize.constantize }
associated_user_groups = UserGroups.constants.map { |user_groups_constant| UserGroups.const_get(user_groups_constant).to_s }
has_many_through_polymorphs(associated_user_groups)
end
end
我有admin_user
和standard_user
的其他类,它们与类和表名更改的明显例外相同。为了简洁起见,我省略了它们。
<强>〜/应用/模型/ user_groups / project.rb 强>
module UserGroups
class Project < ApplicationRecord
include Polymorpher
self.table_name = 'user_groups_projects'
Dir[Rails.root + 'app/models/users/*.rb'].map { |f| File.path(f).to_s.sub(Rails.root.to_s + '/app/models/', '').chomp('.rb').camelize.constantize }
associated_users = Users.constants.map { |users_constant| Users.const_get(users_constant).to_s }
has_many_through_polymorphs(associated_users)
end
end
我再一次为team
增加了一个相同的类,同样也有类和表名更改的明显例外。
<强>〜/应用/模型/关系/ relationship.rb 强>
module Relationships
class Relationship < ApplicationRecord
self.table_name = 'relationships_relationships'
after_create { |relationship| create_reverse_relationship(relationship) }
belongs_to :origination_class, polymorphic: true
belongs_to :destination_class, polymorphic: true
validates :origination_class_id, uniqueness: { scope: [:origination_class_type, :destination_class_id, :destination_class_type] }
private
def create_reverse_relationship(relationship)
reverse_origination = relationship.destination_class_type.constantize.find(relationship.destination_class_id)
reverse_destination = relationship.origination_class_type.constantize.find(relationship.origination_class_id)
association_attribute = relationship.origination_class_type.underscore.gsub('/','_').pluralize
if !Relationships::Relationship.exists?(origination_class_type: reverse_origination.class.to_s,
origination_class_id: reverse_origination.id,
destination_class_type: reverse_destination.class.to_s,
destination_class_id: reverse_destination.id)
eval "reverse_origination.#{ association_attribute } << reverse_destination"
end
end
end
end
<强>〜/应用/模型/关切/ polymorpher.rb 强>
module Polymorpher
extend ActiveSupport::Concern
included do
after_destroy { |origin_object| delete_destination_relationships(origin_object.class.to_s, origin_object.id) }
def delete_destination_relationships(object_class_name, object_id)
Relationships::Relationship.where(destination_class_type: object_class_name, destination_class_id: object_id).delete_all
end
end
module ClassMethods
def has_many_through_polymorphs(associated_classes)
has_many :relationships_relationships,
class_name: 'Relationships::Relationship',
as: :origination_class,
dependent: :destroy
associated_classes.each do |associated_class|
has_many associated_class.underscore.gsub('/','_').pluralize.to_sym,
through: :relationships_relationships,
source: :destination_class,
source_type: associated_class
end
end
end
end
<强>〜/分贝/迁移/ 20170127022550_create_users_super_admin_user.rb 强>
class CreateUsersSuperAdminUser < ActiveRecord::Migration[5.0]
def change
create_table :users_super_admin_users do |t|
t.string :email
end
end
end
Users
和UserGroups
命名空间类的所有剩余迁移实际上与此相同,但具有UserGroups
属性的:name
类的迁移除外:email
属性。
<强>〜/分贝/迁移/ 20170127036200_create_relationships_relationship.rb 强>
class CreateRelationshipsRelationship < ActiveRecord::Migration[5.0]
def change
create_table :relationships_relationships do |t|
t.references :origination_class, polymorphic: true, index: { name: 'index_relationships_relationships_on_origination_type_and_id' }
t.references :destination_class, polymorphic: true, index: { name: 'index_relationships_relationships_on_destination_type_and_id' }
t.timestamps null: false
end
add_index :relationships_relationships,
[:origination_class_type, :origination_class_id, :destination_class_type, :destination_class_id],
unique: true,
name: 'index_relationships_relationships_unique_combo_of_all_columns'
end
end
注意:此解决方案的灵感来自此交换中的解决方案:
但是,我没有复制它,因为我无法从其他模型继承我的用户模型(例如链接示例中的PolyRecord),因为它们最终将继承自Devise用户模型。