RailsがSQLクエリを生成する前に、ActiveRecordモデルの属性セッターにその値をtext2ltree()postgres関数でラップさせようとしています。
例えば、
post.path = "1.2.3"
post.save
次のようなものを生成する必要があります
UPDATE posts SET PATH=text2ltree('1.2.3') WHERE id = 123 # or whatever
これを行うための最良の方法は何ですか?
RailsがSQLクエリを生成する前に、ActiveRecordモデルの属性セッターにその値をtext2ltree()postgres関数でラップさせようとしています。
例えば、
post.path = "1.2.3"
post.save
次のようなものを生成する必要があります
UPDATE posts SET PATH=text2ltree('1.2.3') WHERE id = 123 # or whatever
これを行うための最良の方法は何ですか?
編集:上記で探しているものを正確に実現するには、これを使用してモデルファイルのデフォルトのセッターをオーバーライドします。
def path=(value)
self[:path] = connection.execute("SELECT text2ltree('#{value}');")[0][0]
end
次に、上記のコードが機能します。
ActiveRecordの内部とその不可解なメタプログラミングの基盤についてもっと知りたいので、演習として、以下のコメントで説明したことを達成しようとしました。これが私のために働いた例です(これはすべてpost.rbにあります):
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 :: Baseのモジュールを、ロードパスのどこか前のファイルに入れたいと思います。また、データベース関数に渡す引数のタイプを適切に処理する必要があります。最後に、これ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でも移行を使用してクエリ書き換えルールを作成することでこれを行うと思いますが、これは素晴らしい学習体験になりました。うまくいけば、それがうまくいき、私は今それを行う方法について考えるのをやめることができます。