2

我完全误解了rails中的机制......想象一个具有许多属性的产品模型:

class Product < ActiveRecord::Base
    has_many :properties
end

然后,在控制台中,我输入:

p=Product.last #recover the last product created
arr=p.properties #return the properties in an Array
arr.class #return "Array", so it's effectively an Array object.

在 Hirb 中,它给了我:

1.9.3-p385 :161 > arr=p.properties
| id        | name        | presentation  | created_at              | updated_at              | value_type |
+-----------+-------------+---------------+-------------------------+-------------------------+------------+
| 905834907 | internet    | internet      | 2012-09-17 13:37:57 UTC | 2012-10-02 15:46:37 UTC | boolean    |
| 905834906 | three_d     | 3D            | 2012-09-17 13:37:47 UTC | 2012-10-10 13:10:07 UTC | boolean    |
| 161337574 | brand       | Marque        | 2012-05-22 14:13:04 UTC | 2013-03-26 16:12:12 UTC | string     |

ETC...

然后,如果我这样做:

1.9.3-p385 :162 > arr.where(:value_type => "boolean")
  Spree::Property Load (0.8ms)  SELECT "spree_properties".* FROM "spree_properties" INNER JOIN "spree_product_properties" ON "spree_properties"."id" = "spree_product_properties"."property_id" WHERE "spree_product_properties"."product_id" = 1060500665 AND "spree_properties"."value_type" = 'boolean'
+-----------+----------+--------------+-------------------------+-------------------------+------------+
| id        | name     | presentation | created_at              | updated_at              | value_type |
+-----------+----------+--------------+-------------------------+-------------------------+------------+
| 905834907 | internet | internet     | 2012-09-17 13:37:57 UTC | 2012-10-02 15:46:37 UTC | boolean    |
| 905834906 | three_d  | 3D           | 2012-09-17 13:37:47 UTC | 2012-10-10 13:10:07 UTC | boolean    |
| 905834914 | wifi     | wifi         | 2013-03-26 16:13:35 UTC | 2013-03-26 16:13:35 UTC | boolean    |

所以我在数组上运行 where 方法......但是:

tab.method(:where) #returns:
NameError: undefined method `where' for class `Array'

如何在无法识别的对象上执行 where ?我有个想法:

1.9.3-p385 :164 > arr.klass
 => Spree::Property(id: integer, name: string, presentation: string, created_at: datetime, updated_at: datetime, value_type: string) 

但我真的不明白这种机制......这对我来说在面向对象的语言中是全新的。

谢谢你的解释。

酸碱度

4

2 回答 2

0

问题是你实际上并没有得到一个实例,Array而是一个ActiveRecord::Relation. 这是为了让您将所有这些指令链接在一起,如果您尝试访问结果,则会执行底层关系并将其转换为数组。Pry 会自动尝试显示结果,以便触发这些转换。

您可以通过执行以下操作来看到这一点:

[1] pry(main)> Customer.first.contracts ; nil
  Customer Load (0.6ms)  SELECT `customers`.* FROM `customers` LIMIT 1
=> nil

[2] pry(main)> Customer.first.contracts
  Customer Load (0.5ms)  SELECT `customers`.* FROM `customers` LIMIT 1
  Contract Load (0.7ms)  SELECT `contracts`.* FROM `contracts` WHERE `contracts`.`customer_id` = 1
=> [...results...]

[3] pry(main)> class ActiveRecord::Relation
[3] pry(main)*   def to_a
[3] pry(main)*     puts "I converted me (#{(class << self ; self ; end).superclass}) to an Array"
[3] pry(main)*     logging_query_plan do
[3] pry(main)*       exec_queries
[3] pry(main)*     end
[3] pry(main)*   end
[3] pry(main)* end
=> nil

[4] pry(main)> Customer.first.contracts ; nil
I converted me (ActiveRecord::Relation) to an Array
  Customer Load (0.5ms)  SELECT `customers`.* FROM `customers` LIMIT 1
=> nil

[5] pry(main)> Customer.first.contracts
I converted me (ActiveRecord::Relation) to an Array
  Customer Load (0.6ms)  SELECT `customers`.* FROM `customers` LIMIT 1
I converted me (ActiveRecord::Relation) to an Array
  Contract Load (0.6ms)  SELECT `contracts`.* FROM `contracts` WHERE `contracts`.`customer_id` = 1
=> [...results...]

步骤 [1]:只有Customer被获取。-relationcontracts仍未执行,因为 Pry 不尝试访问它(由于; nil

步骤 [2]:与 1 相同。但 Pry 尝试访问关系,因此我们也获取这些关系。

步骤 [3]:我们重写 ActiveRecord::Relation.to_a 方法。我只添加了puts其余的是原始方法(使用 super 会导致循环!)。

步骤 [4]:重复 1. 和 2。现在,每次转换都会触发一条附加消息。

Ruby 中的元编程非常复杂,会引起很多麻烦,但我希望这能让事情变得清晰。

于 2013-03-27T12:05:57.660 回答
0

好问题!

控制台的工作方式有点误导:

p=Product.last #recover the last product created
arr=p.properties #return the properties in an Array
arr.class #return "Array", so it's effectively an Array object.

arr.class返回“Array”,因为控制台并activerecord希望帮助您作为程序员。所以他们做 aarr.all.class而不是arr.class.

做一个arr.to_sql,你会看到这将导致 SQL。Array 对象没有方法to_sql

由于延迟加载,您可以将where(:value_type => "boolean")其用作arr.

查看http://xyzpub.com/en/ruby-on-rails/3.2/queries.html#lazy_loading了解更多关于延迟加载的信息。

于 2013-03-27T11:36:57.640 回答