如何在Rails中借助Ancestry将admin权限传递给子集合
符合Rails风格的集合管理员权限判断实现
要实现「用户成为某集合管理员后自动拥有所有子集合的管理员权限」,无需为子集合创建额外Admin记录,我们可以利用Ancestry gem提供的祖先路径方法,结合数据库层面的存在性查询来实现高效判断。
1. 核心逻辑:通过祖先路径ID查询权限
Ancestry的path_ids方法会返回当前集合及其所有祖先集合的ID数组,我们只需查询用户是否在这些集合的管理员列表中即可。
在Collection模型中添加判断方法
直接在Collection类中实现检查用户是否为当前集合或其祖先管理员的方法:
# app/models/collection.rb class Collection < ApplicationRecord has_ancestry has_many :admins has_many :admin_users, through: :admins, source: :user # 判断指定用户是否为当前集合或其祖先的管理员 def admin_user?(user) Admin.exists?(user_id: user.id, collection_id: path_ids) end end
在User模型中添加对应方法(可选,方便从用户视角判断)
如果需要从用户实例出发判断权限,可以在User类中补充:
# app/models/user.rb class User < ApplicationRecord has_many :admins has_many :collections, through: :admins # 判断当前用户是否为指定集合或其祖先的管理员 def admin_of_collection?(collection) Admin.exists?(user_id: id, collection_id: collection.path_ids) end end
2. 用作用域复用查询逻辑(更符合Rails最佳实践)
为了让查询逻辑更复用、更具可读性,可以在Admin模型中定义作用域:
# app/models/admin.rb class Admin < ApplicationRecord belongs_to :collection belongs_to :user # 筛选指定集合及其祖先的管理员记录 scope :for_collection_and_ancestors, ->(collection) { where(collection_id: collection.path_ids) } end
之后就可以在Collection的方法中调用作用域:
# collection.rb def admin_user?(user) user.admins.for_collection_and_ancestors(self).exists? end
3. 性能优化:添加联合索引
由于频繁需要按user_id和collection_id组合查询,建议为admins表添加唯一联合索引,既提升查询速度,又避免重复的管理员记录:
rails generate migration AddIndexToAdminsOnUserIdAndCollectionId
生成的迁移文件内容修改为:
class AddIndexToAdminsOnUserIdAndCollectionId < ActiveRecord::Migration[7.0] def change add_index :admins, [:user_id, :collection_id], unique: true end end
执行迁移:
rails db:migrate
使用示例
# 检查用户是否为某个集合的管理员(含祖先) collection = Collection.find(1) user = User.find(2) collection.admin_user?(user) # 返回true/false # 从用户视角检查 user.admin_of_collection?(collection) # 返回true/false
内容的提问来源于stack exchange,提问作者Steven Clontz
相关产品推荐
相关产品推荐

