【问题标题】:Rails: Why can't you set an association to nil in a where clause?Rails:为什么不能在 where 子句中将关联设置为 nil?
【发布时间】:2011-03-05 21:32:09
【问题描述】:

我有属于收藏和用户的照片。照片始终属于用户,但不能分配给收藏。

在我的控制器中,这非常有效:

@collection_photos = Photo.where( :collection => @collection, :user => current_user )

但是,这失败了……

@other_photos = Photo.where( :collection => nil, :user => current_user )

...但这有效:

@other_photos = Photo.where( :collection_id => nil, :user => current_user )

当 collection 设置为 nil 时,我收到以下错误消息:No attribute named 'collection' exists for table photos

如果我传递一个对象,它知道从符号:collection 中搜索collection_id,但如果我不传递一个对象,它似乎不知道关联。

我理解正确吗?谁能更好地解释一下为什么:collection=>nil 不起作用?

【问题讨论】:

  • 好问题。我不得不深入兔子洞。

标签: ruby-on-rails ruby-on-rails-3 activerecord where


【解决方案1】:

当您使用将条件传入ActiveRecord时,它实际上会尝试分析您传入的对象,它是一个字符串吗?数组?哈希?字符串、数组或哈希中的内容是什么?

在你的情况下是一个哈希,所以它试图分析哈希中的内容,在第一个语句(有效)中,你传入一个模型实例作为值,所以它试图查找是否有任何关联映射到您指定的键,瞧,它找到了它,一切都按计划进行

在第二种情况下,你传入 nil 作为值,现在 ActiveRecord 看到它是一个 nil 对象,所以它决定它不是一个关联。请注意,它不查看键,而只查看值,因此它会尝试查找是否有任何列映射到键,但找不到,返回错误

在最后一种情况下,你传入 nil 作为值,同样的,它试图找到映射到 :collection_id 的列,因此它在 SQL 语句中作为值传入 nil,并且它成功返回

所以这只是 ActiveRecord 采取的一个不幸的考虑,导致第二种情况不起作用 =)

希望这可以澄清! =D

【讨论】:

  • 所以,这是对代码的一个很好的解释,它肯定验证了我仅通过阅读 Jordan 发布的链接上的代码的理解。作为一个后续问题,你能想出一个简单的方法来解决这个问题吗?我有兴趣在这里启用更一致的语法,除非有一个主要原因不这样做。
  • 实际上我不建议您修补代码,因为它会给您的项目带来一些陷阱,例如当您更新 Rails,当您与几个程序员一起工作时,当您查看其他程序员的示例代码等时,想要改变框架提供的默认行为的问题太多了......对我来说,你的第三种方式做足够了,它实际上比想要做第一种方式更清楚......只是我的意见=)
  • 更进一步,在您的情况下,您尝试分析值为 nil 时的值,这意味着对于每个查找为 nil 的列的查询,您都在施加一些开销,因为您必须检查是否存在列中的关联......虽然不知道你是否让我明白这一点
【解决方案2】:

我的猜测是,它就像著名的 rails .find vs .find_by_id

.find 旨在在找不到任何关联时抛出异常。

如果没有找到任何关联,.find_by_id 只会返回 nil。

所以在您的 .where 语句中,当您搜索集合时,它可能会将其视为 .find 并且当您按 collection_id 搜索时,它将返回 nil 就像 .find_by_id 一样,如果它找不到任何关联的集合。

我不确定这两种方法在 Activerecord 的内部工作方式上有何不同,但它们旨在对 nil 结果做出不同的反应。

【讨论】:

  • 这可能是个不错的猜测,谢谢!我很想知道是否有人真的知道为什么它不起作用。
【解决方案3】:

我认为您的答案在ActiveRecord::PredicateBuilder.build_from_hash。里面有一个case语句检查hash中每个值的类,它专门寻找ActiveRecord::Relation

【讨论】:

  • 嘿,确实!试图找到链接但无法获得它,一个向上=)
  • 谢谢。在 Github 上进行了一些探索
  • 感谢您的链接,我知道它是如何工作的。作为一个后续问题(和我问 Staelen 的问题一样),你能想出一个简单的方法来解决这个问题吗?我有兴趣在这里启用更一致的语法,除非有一个主要原因不这样做。
  • 对我来说,真正了解那里发生的事情为时已晚。一个好的起点是when Class。您需要检查column 是否为reflect_on_all_associations.map(&:name).include?(column.to_sym) and value.nil?,然后找出attribute.eq(value.name)attribute.in(value.arel.ast) 在做什么。不过我会测试性能是否受到影响,因为我不确定reflect_on_all_associations 的影响
【解决方案4】:

这似乎不再是 Rails 4 中的问题。例如以下代码

@other_photos = Photo.where( :collection => nil, :user => User.first )

会运行

User Load (Xms)  SELECT  "users".* FROM "users"   ORDER BY "users"."id" ASC LIMIT 1
Photo Load (Xms)  SELECT "photos".* FROM "photos"  WHERE "photos"."collection_id" IS NULL AND "photos"."user_id" = 1

*在 Rails 4.1.1 中测试

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-12
    • 1970-01-01
    相关资源
    最近更新 更多