我正在尝试清理一些被标记为易受 SQL 注入(inject)攻击的代码。因此,我将大量原始 SQL 查询字符串转换为 ActiveRecord 的方法。当我想将排序 (order
) 应用于嵌套属性时,我在寻找等效查询时遇到了问题。
如果我有一个 Account
和 User
对象,并且我想按 user_id
对结果进行排序,我可以 这样做(这是目前的代码):
->(direction) { Account.joins(:users).order("users.id #{direction}").first }
但是,这很容易受到 SQL 注入(inject)攻击。
我知道,如果我按 Account
的属性进行排序,您只需将哈希传递给 order
->(direction) { Account.joins(:users).order(created_at: direction).first }
但是,使用字符串作为排序依据的属性(因为它是嵌套的)不会产生正确的查询:
->(direction) { Account.joins(:users).order('users.id': direction).first }
# SELECT `accounts`.* FROM `accounts` INNER JOIN `users` ON `users`.`account_id` = `accounts`.`id` ORDER BY `accounts`.`users.id` DESC LIMIT 1
并且使用嵌套散列也不起作用
->(direction) { Account.joins(:users).order(users: {id: direction}).first }
我知道我可以通过一些额外的逻辑强制 direction
成为可接受的值之一,但我想知道是否有一种简单的方法可以通过我遗漏的 ActiveRecord 查询来做到这一点。
最佳答案
我担心只能通过字符串支持通过加入的协会进行排序。
但是,有可能merge允许你写的范围:
->(direction) { Account.joins(:users).merge(User.order(id: direction})).first }
这确实会阻止 [:asc, :desc, :ASC, :DESC, "asc", "desc", "ASC", "DESC"]
以外的方向值(摘自ArgumentError 消息)。在我看来,这是以降低可读性为代价的,但可以通过将顺序定义为 User
类中的命名范围来尝试缓解问题并同时提高可重用性。
class User
...
self.ordered_by_id(direction)
order(id: direction})
end
...
end
这使您能够使用
->(direction) {
Account.joins(:users).merge(User.ordered_by_id(direction)).first
}
诚然,在当前示例中这还没有发挥作用,但是使用具有更复杂范围的 merge
可以大大减少重复。
关于mysql - 使用 JOIN 时 ActiveRecord 查询中 ORDER 的动态方向,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46394845/