2012-04-27 2 views
8

ActiveRecordモデルでアトリビュートセッターを作成しようとすると、その値がtext2ltree()postgres関数にラップされてからsqlクエリーが生成されます。例えば アトリビュートセッターがSQL関数で値を送信する方法

post.path = "1.2.3" 
post.save 

生成する必要があります

UPDATE posts SET PATH=text2ltree('1.2.3') WHERE id = 123 # or whatever 

これを行うための最善の方法は何のようなもの?

+0

Postgres関数(ストアドプロシージャ)を作成してから、 'SELECT myfunc( '1.2.3')'を呼び出すことができます。あなたがそのルートに興味があるなら、私は例を提供することができます。 –

答えて

2

EDIT:は正確にあなたが上記の探しているものを達成するために、あなたのモデルファイルのデフォルトのセッターをオーバーライドするために、これを使用すると思います:

def path=(value) 
    self[:path] = connection.execute("SELECT text2ltree('#{value}');")[0][0] 
end 

は、その後、あなたが上記の持っているコードは動作します。

私はActiveRecordの内部構造と不可能なメタプログラミングの基礎について学ぶことに興味がありますので、練習として以下のコメントで説明したことを達成しようとしました。

module DatabaseTransformation 
    extend ActiveSupport::Concern 

    module ClassMethods 
    def transformed_by_database(transformed_attributes = {}) 

     transformed_attributes.each do |attr_name, transformation| 

     define_method("#{attr_name}=") do |argument| 
      transformed_value = connection.execute("SELECT #{transformation}('#{argument}');")[0][0] 
      write_attribute(attr_name, transformed_value) 
     end 
     end 
    end 
    end 
end 

class Post < ActiveRecord::Base 
    attr_accessible :name, :path, :version 
    include DatabaseTransformation 
    transformed_by_database :name => "length" 

end 

コンソール出力:

1.9.3p194 :001 > p = Post.new(:name => "foo") 
    (0.3ms) SELECT length('foo'); 
=> #<Post id: nil, name: 3, path: nil, version: nil, created_at: nil, updated_at: nil> 

私はあなたがinclude ActiveRecordの中のモジュールにしたいと思います推測現実の生活の中でここで(これはすべてのpost.rbである)私のために働いた例を示します。 :ベースは、ロード・パスの前のどこかのファイルにあります。また、データベース関数に渡す引数の型を適切に処理する必要があります。最後に、私はconnection.executeが各データベースアダプタによって実装されていることを知ったので、結果にアクセスする方法はPostgresで異なるかもしれません(この例は、結果セットがハッシュの配列と最初のデータレコードのキーとして返されるSQLite3です) 。ある0]

このブログの記事は非常に有用だった:

http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/

Railsは、プラグイン・オーサリングのためのガイドだった:

http://guides.rubyonrails.org/plugins.html

また、Postgresでは、まだ移行を使ってクエリ書き換えルールを作成していると思いますが、これは素晴らしい学習体験のために作られたものです。うまくいけばそれは機能し、今私はそれをどうやって行うのか考えることをやめることができます。

+1

PostgreSQLのクエリー・リライト・エンジンを使うのと全く違ったやり方です。トリガーのようなビット。値を関数に渡すように更新を書き換えるルールを設定するだけです。利点は、モデルがきれいで、データベースへの呼び出しを1回しか行わないことです。欠点はデータベースに依存しないことであり、明らかにあなたの場合は問題ではないかもしれません。私は例を投稿するための構文に慣れていませんが、ここにはドキュメントへのリンクがあります: http://www.postgresql.org/docs/8.4/static/sql-createrule。htmlの –

+0

私はここに猿のパッチが含まれているこれを行うより効率的な方法である必要がありますように感じるActiveRecord/Arel –

+0

あなたがパフォーマンスを意味する "効率的"であれば、 Railsでdbへの2回の呼び出しを回避する方法を実装する方法はわかりません。 「効率的」でDRYer /より表現力豊かなことを意味する場合(これはあなたがこれをやりたがっている唯一の場所ではないと仮定して)、それはオプションです。 –

関連する問題