有条件地为活动记录中的多对多关系创建新记录

时间:2018-12-17 22:48:21

标签: ruby-on-rails rails-activerecord

我正在与RoR一起从事ActiveRecord项目,对这些技术我还很陌生。

我有两个模型:

User.rb

class User < ApplicationRecord
...
has_and_belongs_to_many :roles
...

Role.rb

class Role < ApplicationRecord
...
has_and_belongs_to_many :users
...

所以现在我要创建一个新角色:

Role.where(name: 'my_new_role').first_or_create!

并且我想创建一个rake任务,该任务将为已经拥有admin角色的用户和所有其他没有unfit_role角色的用户添加此角色。

如果我使用纯SQL进行此操作,则很可能会执行一次选择以收集要更新的所有用户ID,然后对收集的所有ID执行另一个更新查询。

但是我觉得ActiveRecord有一些我还不太了解的细节,但是我决定尝试其他方法。

当前我有以下代码:

new_role = Role.where(name: :my_new_role).first

User.all.each do |entity|
  entity.roles << new_role if entity.has_role?('admin')
  entity.roles << new_role if !entity.has_role?('unfit_role')
end

has_role是一种简单的帮助程序方法,它一次选择所有角色,然后使用include?检查给定角色是否在具有角色的所选列表中。

基于1-2次尝试运行,看来这实际上是可行的,但我真的不喜欢该实现。通常,我检查某个用户(实体)是否具有给定角色的方式。我觉得这是执行相对简单的任务的效率很低的方法,所以我想知道如何以其他方式实现它,从而更快。

1 个答案:

答案 0 :(得分:0)

您可以使用Postgres aggregate functions来提高查询效率:

new_role = Role.where(name: 'my_new_role').first!
admin_role = Role.where(name: 'admin').first!
unfit_role = Role.where(name: 'unfit_role').first!

User.joins(:roles).group(:id).having("bool_or(roles.id = #{admin_role.id}) or not bool_or(roles.id = #{unfit_role.id})").each do |entity|
  entity.roles << new_role
end