使用ActiveRecord帮助程序删除VS查找孤儿

时间:2015-10-16 21:45:48

标签: ruby-on-rails postgresql ruby-on-rails-4 activerecord arel

我试图删除所有不再有organizations的{​​{1}}。

使用以下代码,我可以找到我想要删除的所有记录:

users

当我添加Organization.includes(:users) .where(users: { id: nil }) .references(:users) 时,如果我没有包含delete_all,我会得到同样的错误:

references

我可以用纯SQL编写解决方案,但是我不明白为什么当我添加PG::UndefinedTable: ERROR: missing FROM-clause entry for table "users" 语句时Rails不会保留对users的引用。

以下是一些更多细节:

delete_all

2 个答案:

答案 0 :(得分:2)

我发现includes仅对急切加载很有用(并且很少处理我的案例),当与references结合使用时,它会生成某些内容完全疯了(使用类似tN_rM之类的每个字段别名),即使它实际上有LEFT OUTER JOIN ...如果 可以帮助<一旦delete_all出现,强>消失!

我发现使用exists会更清晰,更简单。它是Arel(并且没有任何意义可以避免它,无论如何它都在ActiveRecord的引擎盖下),但它只是一个很小的部分,它几乎没有引人注目:

Organization.where(
  User.where('users.organization_id = organizations.id').exists.not
)

或者,如果这个SQL字符串看起来不太好,请使用更多的Arel,这样会引人注意:

Organization.where(
  User.where(organization_id: Organization.arel_table[:id]).exists.not
) # I tend to extract these   ^^^^^^^^^^^^^^^^^^^^^^^ into local variables

它处理链接.delete_all就好了,因为它不是(语法上)一个连接,即使它实际上等同于一个连接。

这背后的魔力

SQL有一个EXISTS运算符,其功能与连接类似,但无法从连接表中选择字段。它形成一个有效的布尔表达式,可以否定投入WHERE - 条件

在“无SQL”表单中,我使用表达式“表的列”,结果证明它可用于Rails的哈希条件。这是一个偶然的发现,是Arel的少数用途之一,它不会使代码过于庞大。

答案 1 :(得分:0)

我不确定您打算如何在MVC框架中实现此功能,但通过模型操作进行组织清除似乎很简洁。每当用户被删除时,检查组织是否有剩余的成员。

User.rb中的

class User < ActiveRecord::Base
  before_destroy :close_user
  ...

 def user_organization
   Organization.where(user_id: id)
 end

  private
    def close_user
        unless user_organization.users.any? 
          user_organization.destroy
        end
    end

end

已添加将回调删除解决方案应用于属于多个组织成员的用户

如果用户有多个组织

class User < ActiveRecord::Base
      before_destroy :close_user
      ...

     def user_organizations
       Organization.where(user_id: id)
     end

      private
        def close_user
            user_organization.find_each do |organization| 
            unless organization.users.any? 
              organization.destroy
            end
        end

    end

警告:这没有经过测试,语法没有失败。我没有足够的数据来测试它,但我认为它会起作用。但这意味着在每次删除用户之后运行此操作,这是一个系统架构决策。如果它是一个选项,它可能值得一试。