我正试图通过加载一些sql调用来加速我的应用程序。我正在使用CanCan gem来处理我的管理员授权。我有一个包含三个不同角色的Roles表和一个多对多表roles_users。每次页面加载CanCan能力集时,它会执行三个单独的SQL查询。
Role Load (0.6ms) SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id"
= "roles_users"."role_id" WHERE "roles_users"."user_id" = 2 AND "roles"."name" = 'admin'
LIMIT 1
Role Load (0.4ms) SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id"
= "roles_users"."role_id" WHERE "roles_users"."user_id" = 2 AND "roles"."name" =
'manager' LIMIT 1
Role Load (0.3ms) SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id"
= "roles_users"."role_id" WHERE "roles_users"."user_id" = 2 AND "roles"."name" = 'user'
LIMIT 1
我尝试过设置default_scope:include => :User类中的角色,还包括:has_and_belongs_to_many调用中包含的内容。
我在哪里可以急切加载Roles表以仅使用1个SQL查询?
答案 0 :(得分:2)
看起来你可能有一些如下所示的代码。也就是说,它需要一个字符串或符号来比较角色记录中的某个值,而不是角色本身。所以寻找这样的东西:
def do_i_have_role(role_name_to_check)
self.roles.detect do |role|
role.name == role_name_to_check
end
end
你可以在这里做一些急切的加载来修复它,但另一种方法来修改这个猫是重构您的查询。也就是说,首先查找角色对象,然后查看您的帐户是否具有该角色。
def do_i_have_role(role_name_to_check)
role = Role.where(:name => role_name_to_check)
self.roles.include? role
end
现在它只是对角色数据库的一次点击而不是3次(对帐户表的点击以及对帐户表的另一次点击,您可能永远无法避免)。
答案 1 :(得分:1)
您可以通过显示用于查询cancan系统的代码(api调用cancan)来改进您的问题。
看起来您正在进行单独的查询,以查看用户是否在管理员,经理或用户类别中。
您应该命令查询首先查询最可能的用户类型。例如,如果赔率是一个人很可能是一个用户,那么首先查询该用户。
更好的是,缓存用户在会话中的角色。这样你只会进行一次查询。
<强>加强>
您可以在登录/注销挂钩后使用设计来设置会话中的值。您可以使用Action Controller filters查找角色(来自会话)。 Session docs
安全问题
根据您的安全级别,您可能需要涵盖用户被删除或降级的情况,并且您希望立即进行更改(而不是等到用户下次登录时)
这样做可能很棘手。一些技巧:
使用常量您可以在会话(或内存缓存)中存储role_ids。但是将它们与常数进行比较。例如user.role_id === ROLE_ADMIN
您可以通过从db中查找role_ids来在运行时设置常量。确保在初始化系统中每个rails进程执行一次此操作。每次传入请求都不会一次。