我正在尝试在Rails中以非常快速和肮脏的方式运行查询,而不会将模型的其余部分放在适当的位置。我知道这是不好的做法,但我需要在紧迫的时间内快速得出结果,直到我得到整个解决方案。
根据重量,我的商品有运费。重量存储在商品中,价格存储在表shipping_zone_prices中,而我目前所做的只是查找与重量比销售商品重的第一行相关的价格:
class Item < ActiveRecord::Base
def shipping_price
item_id = self.id
shipping_price = ShippingZonePrice.find_by_sql(
"SELECT z.price as price
FROM shipping_zone_prices z, items i
WHERE i.id = '#{item_id}'
AND z.weight_g > d.weight
ORDER BY z.weight_g asc limit 1")
end
end
这种作品。 SQL可以完成这项工作,但是当按照以下方式插入应用程序时:
<%= @item.shipping_price %> Shipping
我显示以下内容:
[#<ShippingZonePrice price: 12>] Shipping
在此示例中,“12”是从数据库中提取的价格,并且是正确的。 @ item.shipping_price.class返回'Array'。尝试使用[0](或任何其他整数)访问数组会返回空白。
是否有另一种方式来访问它,或者我错过了一些基本的东西?
答案 0 :(得分:7)
由于您正在定义实例方法,我认为它应该返回price
(如果存在)或nil
尝试这样的事情:
def shipping_price
ShippingZonePrice.find_by_sql(
"SELECT z.price as price
FROM shipping_zone_prices z, items i
WHERE i.id = '#{self.id}'
AND z.weight_g > d.weight
ORDER BY z.weight_g asc limit 1").first.try(:price)
end
然后这应该适合你:
@item.shipping_price
需要first.try(:price)
部分,因为find_by_sql
可能会返回一个空数组。如果您尝试在空数组上执行first.price
之类的操作,则会在NoMethodError: undefined method 'price' for nil:NilClass
的行中出现异常。
答案 1 :(得分:5)
这是因为find_by_sql
返回的是模型,而不是数据。如果您想直接获取有问题的数据,请使用以下内容:
ShippingZonePrice.connection.select_value(query)
connection
提供了许多直接访问实用程序方法,可以获取单个值,单个数组,数组行或哈希行。查看ActiveRecord::ConnectionAdapters::DatabaseStatements
的{{3}}。
在直接编写SQL时,您应该非常小心,不要创建SQL注入错误。这就是为什么通常最好将此方法封装在安全的地方。例如:
class ShippingZonePrice < ActiveRecord::Base
def self.price_for_item(item)
self.connection.select_value(
self.sanitize_sql(
%Q[
SELECT z.price as price
FROM shipping_zone_prices z, items i
WHERE i.id=?
AND z.weight_g > d.weight
ORDER BY z.weight_g asc limit 1
],
item.id
)
)
end
end
答案 2 :(得分:3)
@item.shipping_price.first.price
或
@item.shipping_price[0].price
感谢Atastor指出这一点!
在AS price
中使用find_by_sql
时,price
会成为结果的属性。
答案 3 :(得分:1)
如果不是因为你说你试过并且访问失败[0]
我会说你要放
@item.shipping_price.first.price # I guess BSeven just forgot the .first. in his solution
进入视图......奇怪
答案 4 :(得分:1)
所以,我有一个hacky解决方案,但它很好。 创建一个与函数具有相同输出并引用它的表,然后调用一个执行find_by_sql的函数来填充模型。
创建一个虚拟表:
CREATE TABLE report.compliance_year (
id BIGSERIAL,
year TIMESTAMP,
compliance NUMERIC(20,2),
fund_id INT);
然后,创建一个使用空表的模型:
class Visualization::ComplianceByYear < ActiveRecord::Base
self.table_name = 'report.compliance_year'
def compliance_by_year(fund_id)
Visualization::ComplianceByYear.find_by_sql(["
SELECT year, compliance, fund_id
FROM report.usp_compliance_year(ARRAY[?])", fund_id])
end
end
在您的控制器中,您可以填充它:
def visualizations
@compliancebyyear = Visualization::ComplianceByYear.new()
@compliancefunds = @compliancebyyear.compliance_by_year(current_group.id)
binding.pry
end
然后,您可以看到它填充了您需要的内容:
[1] pry(#<Thing::ThingCustomController>)> @compliancefunds
[
[0] #<Visualization::ComplianceByYear:0x00000008f78458> {
:year => Mon, 31 Dec 2012 19:00:00 EST -05:00,
:compliance => 0.93,
:fund_id => 1
},
[1] #<Visualization::ComplianceByYear:0x0000000a616a70> {
:year => Tue, 31 Dec 2013 19:00:00 EST -05:00,
:compliance => 0.93,
:fund_id => 4129
},
[2] #<Visualization::ComplianceByYear:0x0000000a6162c8> {
:year => Wed, 31 Dec 2014 19:00:00 EST -05:00,
:compliance => 0.93,
:fund_id => 4129
}
]