Rails中的find_by_sql,访问生成的数组

Chr*_*yne 5 ruby-on-rails

我试图在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
Run Code Online (Sandbox Code Playgroud)

这种作品.SQL完成了这项工作,但是当插入应用程序时,如下所示:

 <%= @item.shipping_price %> Shipping
Run Code Online (Sandbox Code Playgroud)

我显示以下内容:

[#<ShippingZonePrice price: 12>] Shipping
Run Code Online (Sandbox Code Playgroud)

在此示例中,"12"是从数据库中提取的价格,并且是正确的.@ item.shipping_price.class返回'Array'.尝试使用[0](或任何其他整数)访问数组会返回空白.

有没有其他方式来访问它,或者我错过了一些基本的东西?

Kyl*_*yle 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
Run Code Online (Sandbox Code Playgroud)

那么这对你有用:

@item.shipping_price
Run Code Online (Sandbox Code Playgroud)

first.try(:price)部分是必要的,因为find_by_sql可能会返回一个空数组.如果你试图first.price在空数组上做类似的事情,你会得到一个例外NoMethodError: undefined method 'price' for nil:NilClass.


tad*_*man 5

这是因为find_by_sql返回模型而不是数据.如果您想直接获取有问题的数据,请使用以下内容:

ShippingZonePrice.connection.select_value(query)
Run Code Online (Sandbox Code Playgroud)

有许多可用的直接访问实用程序方法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
Run Code Online (Sandbox Code Playgroud)