使用 ActiveRecord 助手删除 VS 查找孤儿
Deleting VS Finding Orphans using ActiveRecord helpers
我正在尝试删除不再有 users
的所有 organizations
。
使用下面的代码,我可以找到我想删除的所有记录:
Organization.includes(:users)
.where(users: { id: nil })
.references(:users)
当我添加 delete_all
时,我得到了与不包含 references
:
时相同的错误
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "users"
我可能可以用纯 SQL 编写解决方案,但我不明白为什么 Rails 在我添加 [=15] 时没有保留对 users
的引用=]声明。
以下是更多详细信息:
Organization:
has_many :users
User:
belongs_to :organization
我发现 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
运算符,它在功能上类似于连接,除了无法从连接的 table 中选择字段。它形成一个有效的布尔表达式,可以 取反 和 抛入 WHERE
-conditions.
在 "SQL-free" 形式中,我使用了表达式 "column of a table",它在 Rails' 散列条件中可用。这是一个偶然的发现,是 Arel 为数不多的不会使代码过于庞大的用途之一。
我不确定您打算如何在 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
注意:这没有经过测试,语法没有失败。我没有数据来全面测试它,但我认为它会起作用。但它意味着 运行 这个动作在每个用户删除之后,这是一个系统架构决定的。如果这是一个选择,它可能值得一试。
我正在尝试删除不再有 users
的所有 organizations
。
使用下面的代码,我可以找到我想删除的所有记录:
Organization.includes(:users)
.where(users: { id: nil })
.references(:users)
当我添加 delete_all
时,我得到了与不包含 references
:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "users"
我可能可以用纯 SQL 编写解决方案,但我不明白为什么 Rails 在我添加 [=15] 时没有保留对 users
的引用=]声明。
以下是更多详细信息:
Organization:
has_many :users
User:
belongs_to :organization
我发现 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
运算符,它在功能上类似于连接,除了无法从连接的 table 中选择字段。它形成一个有效的布尔表达式,可以 取反 和 抛入 WHERE
-conditions.
在 "SQL-free" 形式中,我使用了表达式 "column of a table",它在 Rails' 散列条件中可用。这是一个偶然的发现,是 Arel 为数不多的不会使代码过于庞大的用途之一。
我不确定您打算如何在 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
注意:这没有经过测试,语法没有失败。我没有数据来全面测试它,但我认为它会起作用。但它意味着 运行 这个动作在每个用户删除之后,这是一个系统架构决定的。如果这是一个选择,它可能值得一试。