何时在Ruby on Rails ActiveRecord上传递实例代替id?

时间:2013-01-24 03:08:37

标签: ruby-on-rails-3 activerecord

示例场景: 模型工人belongs_to模型Bucket。

请参阅以下查询:

1.9.3p194 :045 > Worker.where(bucket_id: Bucket.first).count
   (0.7ms)  SELECT COUNT(*) FROM "workers" WHERE "workers"."bucket_id" = 1
 => 38
1.9.3p194 :046 > Worker.where(bucket_id: Bucket.first.id).count
   (0.7ms)  SELECT COUNT(*) FROM "workers" WHERE "workers"."bucket_id" = 1
 => 38

1.9.3p194 :047 > Worker.new bucket_id: Bucket.first
 => #<Worker id: nil, email: nil, created_at: nil, updated_at: nil, bucket_id: nil>
1.9.3p194 :048 > Worker.new bucket_id: Bucket.first.id
 => #<Worker id: nil, email: nil, created_at: nil, updated_at: nil, bucket_id: 2>

正如您所看到的,在where函数的情况下,传递Bucket.first之类的实例可以代替确切的id。所以人们会认为它也适用于new函数。相反,它无声地失败!

为什么这样做?

3 个答案:

答案 0 :(得分:3)

我相信这发生在ActiveRecord::PredicateBuilder。如果值是ActiveRecord :: Base对象,您可以在其中看到它将在其上调用idnew方法不会触发此代码,因此它的行为会有所不同。

我更喜欢明确并直接传递id。但是upcoming in Rails 4你可以这样做:

Worker.where(bucket: Bucket.first).count

与初始化非常相似:

Worker.new(bucket: Bucket.first)

一般情况下,如果您设置/匹配的属性在id中结束,我建议传递_id

更新:我还想指出,初始化将继承where条件。所以这将适用于Rails 3.2:

Worker.where(bucket_id: Bucket.first).new

我假设最终会通过PredicateBuilder,但不确定。即使这有效,我也不推荐它。

答案 1 :(得分:0)

根据我的理解,Bucket.first会返回一个即时对象,但Bucket.first.id只会返回即时对象的ID。

因此,当您创建Worker.new并传递bucket_id: Bucket.first时,实际上它将无效,

Worker.new bucket_id: Bucket.first.id 

它将传递正确的参数以创建新的Worker。

答案 2 :(得分:0)

当您使用.new时,您正在创建一个空白记录,但它还没有ID。使用Active Record时,这经常用于表单。

这与.create不同,如果给定所需的有效参数,则会创建带有ID的记录。同样,选择带有Bucket.first的记录会获得具有ID的现有记录。

如果您使用.new,请设置所需参数,然后执行.save,然后您将获得一个ID。