我完全误解了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 |
等...
然后,如果我这样做:
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'
如何在无法识别它的对象上进行操作?我有一个想法:
1.9.3-p385 :164 > arr.klass
=> Spree::Property(id: integer, name: string, presentation: string, created_at: datetime, updated_at: datetime, value_type: string)
但是我真的不明白这个机制......对于我来说,这是一种面向对象语言的新东西。
感谢您的解释。
PH
答案 0 :(得分: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
希望作为程序员帮助您。因此,他们会使用arr.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以了解有关延迟加载的更多信息。
答案 1 :(得分: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
。 contracts
- 关系仍未执行,因为Pry没有尝试访问它(由于; nil
)
步骤[2]:与1.相同但Pry尝试访问该关系,因此我们也会获取这些关系。
步骤[3]:我们覆盖ActiveRecord :: Relation.to_a方法。我只添加了puts
,其余的是原始方法(使用super会导致循环!)。
步骤[4]:重复1.和2.现在,每次转换都会触发其他消息。
Ruby中的元编程非常复杂并且引起很多麻烦,但我希望这能使事情变得清晰。