【问题标题】:In Rails, can I filter one-to-many relationships without updating the database在 Rails 中,我可以在不更新数据库的情况下过滤一对多关系吗
【发布时间】:2019-11-13 17:51:23
【问题描述】:

我正在使用 Rails 5,并且我希望能够过滤一对多关系以仅将子项的子集发送到客户端。数据模型非常标准,看起来像这样:

class Parent < ApplicationRecord
  has_many :children, class_name: 'Child'
end

class Child < ApplicationRecord
  belongs_to :parent
end

当客户端拨打电话时,我只想为Parent 返回一些 Child 实例。

这也很复杂,因为应该返回 Child 对象的逻辑非常复杂,所以我在 Ruby 中而不是在数据库中进行。

每当我执行以下操作时,Rails 都会尝试更新数据库以删除关联。我不想更新数据库。我只想在将结果发送给客户端之前对其进行过滤。

parent.children = parent.children.reject { |child| child.name.include?('foo') }

有没有办法做到这一点?

【问题讨论】:

    标签: ruby-on-rails ruby activerecord ruby-on-rails-5


    【解决方案1】:

    Parent模型中添加实例方法

    def filtered_children
      children.where.not("name like ?", '%foo%')
    end
    

    在需要的地方调用filtered_children,重置现有的关联实例变量没有意义。相同的查询会被缓存,因此无论您调用它们一次还是多次都没有关系。但是您始终可以memoize 方法的输出以确保该方法不会在第二次以后再次评估,

    def filtered_children
      @filtered_children ||= children.where.not("name like ?", '%foo%')
    end
    

    希望有帮助!

    【讨论】:

    • 感谢您的回复。我不确定这是否可以使用。这将执行一个全新的查询。这可能会被调用 50 多次。我想避免 50 次往返数据库。为避免这种情况,我急切地加载所有父和子实例。
    • 您到底想实现什么,相同的查询被缓存,因此无论您调用一次还是多次都没有关系。或者你可以记住它们以停止被多次调用,检查我的编辑。
    • 实际上我有三层一对多关系。我想急切地请求所有记录(包括一些额外的急切电话)。然后,我想在可以进行单元测试的 Ruby 代码中过滤这些结果的每个级别。
    • 其他一些潜在的解决方案包括... 1. 用一个简单的数组替换 ActiveRecord::Relation::CollectionsProxy。我一直无法弄清楚如何做到这一点。 2. 从数据库会话中分离所有实例。这似乎也不可能做到。
    【解决方案2】:

    正在发生数据库更新,因为过滤的记录被分配回parent.children。相反,可以使用另一个变量。

    filtered_children = parent.children.reject { |child| child.name.include?('foo') }

    【讨论】:

    • 这不起作用,因为“拒绝”功能会修改数据库。
    • @EvilJoe Reject 函数不会从数据库中删除。将剩余的对象分配给parent.children 将从数据库中删除。
    • 我确信它确实如此,因为这就是促成这篇文章的原因。最后,我必须创建新类并将模型对象层次结构放在这些“服务器端可过滤”类中。然后,我对这些进行了过滤,因为它们使用的是常规数组而不是 ActiveRecord 代理。
    • 啊,你是对的,但我想将对象保留在当前结构中(其中一些被过滤掉)。结构很复杂,结构的副本很难维护。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    • 2021-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多