简体   繁体   English

Ruby:使用字符串插值访问ActiveRecord属性

[英]Ruby: Use string interpolation to access ActiveRecord properties

Is it possible for me to use string interpolation to build up an ActiveRecord query like so: 我是否可以使用字符串插值来建立ActiveRecord查询,如下所示:

query = '.owner.name'
widget = Widget.first
name = "#{Widget" + query + "}"

This obviously doesn't work, but I'm looking for an acceptable alternative. 这显然行不通,但我正在寻找一种可接受的替代方案。

query = '.owner.name'
widget = Widget.first
name = eval "widget#{query}"

Not that this is a good idea, but it does work. 并不是说这是一个好主意,但是它确实有效。

Is 'eval' supposed to be nasty? “ eval”是否令人讨厌?

With great power comes great responsibility (and risk). 强大的力量带来巨大的责任感(和风险)。

Since Rails uses hashes to map columns to methods, and those hashes are "hashes with indifferent access" you can use either a string or a symbol. 由于Rails使用散列将列映射到方法,并且这些散列是“访问无关的散列”,因此可以使用字符串或符号。 Thus: 从而:

widget = Widget.first
widget[:owner] # => returns the same as widget.owner
widget['owner'] # => also returns widget.owner. 

The problem with the above code, is that owner IS PROBABLY a foreign key to another column, so the method name is actually: 上面的代码的问题在于,所有者可能是另一列的外键,因此方法名称实际上是:

widget['owner_id']

which returns an integer that Rails uses to look up the id in the Owners table. 返回一个整数,Rails使用该整数在Owners表中查找ID。 So if you are ONLY stringing together columns that return columns in the Widget table, you can use the above code. 因此,如果仅将返回Widget表中的列的列串在一起,则可以使用上面的代码。 But when you are stringing together query methods it is usually because you are looking up some relationship in the DB and calling a column method on that. 但是,当将查询方法串在一起时,通常是因为您正在数据库中查找某种关系并在其上调用列方法。

Thus you would need to do some sort of recursive loop to take: 因此,您将需要执行某种递归循环:

query = 'owner.name'
q = query.split'.'

And work up something that results in: 并完成以下工作:

q0 = "#{q[0]}_id"
q1 = "#{q[1]}"
Owner.find(widget[q0])[q1]

#Owner Load (0.6ms)  SELECT "owners".* FROM "owners" WHERE "owners"."id" = $1 LIMIT 1  [["id", 9]]
#=> "Joe"

So in theory you split your query string on the dots, add "_id" to everyone except the last one, then chain those together using (potentially nested) .find commands. 因此,从理论上讲,您可以将查询字符串分割成点,然后将“ _id”添加到除最后一个以外的所有其他字符,然后使用(可能嵌套的) .find命令将它们链接在一起。 Can it be done? 能做到吗 Probably. 大概。 Should it be done the way I described? 应该按照我的描述来做吗? Probably not. 可能不是。

If you give a more descriptive use case scenario people might be able to offer a much better answer. 如果您提供更具描述性的用例场景,人们可能会提供更好的答案。 What are you really trying to do in the broader picture? 从更广泛的角度来看,您真正想做什么? There might be some other approach that completely negates the need for doing what you are asking. 可能还有其他方法完全消除了执行您所要询问的需求。

Here is a less stringly typed version. 这是一个不太字符串化的版本。

widget = Widget.first
query = 'owner.name'
name = query.split('.').inject(widget){|sum, meth| sum.send(meth)}

This will apply the methods separated by the dots subsequently to the object passed to inject. 这将随后用点分隔的方法应用于传递到注入的对象。

You could even define the inject call as method in you models. 您甚至可以在模型中将注入调用定义为方法。

But beware that if someone injects a 'class.delete_all' into you properties, all your widgets would be deleted (propably applies to other solutions as well) 但是请注意,如果有人向您的属性中注入“ class.delete_all”,则所有小部件都将被删除(同样适用于其他解决方案)

while you can use aval and get what you want , its not the best way to go around with it. 虽然您可以使用aval并获得所需的东西,但这并不是解决它的最佳方法。

So. 所以。 it you are looking to append active record queries conditionally you should use merge 如果您希望有条件地追加活动记录查询,则应使用合并

as an overview in your case (not the best example, but you get the idea) 作为您的案例的概述(不是最佳示例,但您知道了)

widget = Widget.first
owner = Widget.first.owner

widget_owner = if <some condition>
                  widget.merge(owner)  
               end
widget_owner.try(:name)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM