2012-09-19 40 views
5

モデルの残りの部分を配置せずに、Railsで非常に素早く汚い方法でクエリを実行しようとしています。私はこれが悪い習慣であることは承知していますが、全体の解決策が得られるまでの間、短期間で迅速な結果が必要です。Railsのfind_by_sqlで結果の配列にアクセス

私は、重量に基づいて送料がかかる商品があります。重量は価格がテーブル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」から引き出されている価格ですdb、正しいです。 @ item.shipping_price.classは 'Array'を返します。 [0](または他の整数)を使用して配列にアクセスしようとすると、空白が返されます。

これにアクセスする別の方法はありますか、何か基本的なものがありませんか?

+1

'd.weight'とは何ですか? –

+0

これはi.weightでなければなりません - 私はちょうど 'item'を使ってデータを少し一般的にしようとしていましたが、私はすべての正しい変更をしませんでした –

答えて

6

あなたはインスタンスメソッドを定義しているので、

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 
012:私はそれが存在するか nil

このような何かをしようとした場合、それはpriceを返すべきだと思います

これはあなたのために働く必要があります:find_by_sqlは空の配列を返すことがあるので

@item.shipping_price 

first.try(:price)部分が必要とされています。空の配列にfirst.priceのようなものを実行しようとすると、NoMethodError: undefined method 'price' for nil:NilClassの行に沿って例外が発生します。

3
@item.shipping_price.first.price 

または

@item.shipping_price[0].price 

ことを指摘してくれてありがとうAtastorを!

find_by_sqlAS priceを使用すると、priceは結果のプロパティになります。

1

ないためにあなたが[0] i'ldにアクセスしようとしたし、失敗したことを言っている場合は、ビューに

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

を入れたいと言う...奇妙な

5

find_by_sqlはデータではなくモデルを返します。単一の値を取得することができますconnectionを通して利用可能なダイレクト・アクセスのユーティリティメソッドの数、特異配列、行があります

ShippingZonePrice.connection.select_value(query) 

:あなたは、直接にはこのようなものを使用し、問題のデータのフェッチを行いたい場合配列の配列、またはハッシュの行。ActiveRecord::ConnectionAdapters::DatabaseStatementsdocumentationをご覧ください。

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 
0

だからこそ私はハックな解決策がありましたが、うまくいきます。 関数と同じ出力を持つテーブルを作成して参照してから、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 
} 
] 
関連する問題