5

我试图在 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 返回“数组”。尝试使用 [0](或任何其他整数)访问数组会返回一个空白。

还有其他方法可以访问它,还是我错过了一些基本的东西?

4

5 回答 5

7

由于您正在定义一个实例方法,我认为它应该返回priceif 它存在或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

于 2012-09-19T14:46:27.823 回答
5

这是因为find_by_sql返回一个模型,而不是数据。如果您想直接获取相关数据,请使用以下内容:

ShippingZonePrice.connection.select_value(query)

有许多直接访问实用程序方法可用,connection它们可以获取单个值、单个数组、数组行或哈希行。查看. _ _ ActiveRecord::ConnectionAdapters::DatabaseStatements

与直接编写 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
于 2012-09-19T15:03:39.810 回答
3
@item.shipping_price.first.price

或者

@item.shipping_price[0].price

感谢 Atastor 指出这一点!

当您使用AS pricein 时find_by_sqlprice成为结果的属性。

于 2012-09-19T14:40:13.440 回答
1

如果不是因为你说你尝试访问但失败了,我[0]会说你想放

@item.shipping_price.first.price # I guess BSeven just forgot the .first. in his solution

进入视野……奇怪

于 2012-09-19T14:45:48.400 回答
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
}
]
于 2015-08-26T16:12:57.447 回答