2

假设您有两个 ActiveRecord 模型 - Problem 和 ProblemSet。

你有一个@problem_set 对象,你想检查它是否有某个标题属性的问题。

你可以说:

@problem_set.problems.where(:title => "Adding Numbers").any?

通过运行最佳 SQL返回true

SELECT COUNT(*) FROM "problems" INNER JOIN "problem_sets_problems" ON "problems"."id" = "problem_sets_problems"."problem_id" WHERE "problem_sets_problems"."problem_set_id" = 1 AND "problems"."title" = 'Adding Numbers'

但是,如果 @problem_set 在内存中,即您通过以下方式获得 @problem_set:

@problem_set = ProblemSet.new()
@problem_set.problems << Problem.new(:title = "Adding Numbers")

然后你将无法找到问题(即它返回错误!)。这是因为运行了以下 SQL:

SELECT COUNT(*) FROM "problems" INNER JOIN "problem_sets_problems" ON "problems"."id" = "problem_sets_problems"."problem_id" WHERE "problem_sets_problems"."problem_set_id" IS NULL AND "problems"."title" = 'Adding Numbers'

对持久对象和内存对象进行正确检查的一种可能方法是:

@problem_set.problems.map(&:title).include? "Adding Numbers"

在这两种情况下都正确返回true。但是,在持久对象的情况下,它运行非最佳 SQL(检索所有问题):

SELECT "problems".* FROM "problems" INNER JOIN "problem_sets_problems" ON "problems"."id" = "problem_sets_problems"."problem_id" WHERE "problem_sets_problems"."problem_set_id" = 1

问题:有没有办法使用相同的代码来检查持久对象和内存中的对象,同时运行最佳 SQL 代码?

请注意,允许检查对象持久性的解决方案(但我看不到如何检查集合的脏度)。但是,如果修改了持久对象(即关联集合属性变脏,因此 SQL 查询的结果将过期),它仍然可以工作。

4

1 回答 1

0

好的,我终于解决了。

浏览晦涩的rails函数,我找到了persisted?方法。您可以使用它@problem_set.persisted?来检查对象是持久的还是仅在内存中。

所以答案是:

if @problem_set.persisted?
  @problem_set.problems.where(:title => "Adding Numbers").any?
else
  @problem_set.problems.map(&:title).include? "Adding Numbers"
end

剩下的问题是,关联集合脏的持久对象怎么办?好吧,通过实验,我发现它并没有真正发生。例如,当您将对象添加到集合时,以下操作之一:

@problem_set.problems << Problem.new(:title => "hello")
@problem_set.problems.push Problem.new(:title => "hello")

ActiveRecord 立即保存数据。同样,当您说:

@problem_set.problems.delete(@problem_set.problems[2])

这意味着,虽然没有这样的方法@problem_set.problems_changed?,如果有的话,当前的实现将导致problems_changed?总是返回false

实际上,collection<<, collection.push,collection.delete方法会自动保存(即save自动调用)。

于 2012-08-24T08:33:26.073 回答