2012-04-27 65 views
8

Tôi đang cố gắng đặt một thuộc tính setter trong mô hình ActiveRecord bọc giá trị của nó trong hàm text2ltree() postgres trước khi đường ray tạo truy vấn sql của nó.Làm thế nào để làm cho giá trị setter gửi thuộc tính thông qua hàm SQL

Ví dụ,

post.path = "1.2.3" 
post.save 

Nên tạo ra một cái gì đó giống như

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

cách tốt nhất để làm điều này là gì?

+0

Bạn có thể viết một hàm Postgres (thủ tục lưu sẵn) và sau đó chỉ cần gọi 'SELECT myfunc ('1.2.3')'. Tôi có thể cung cấp một ví dụ nếu bạn quan tâm đến tuyến đường đó. –

Trả lời

2

EDIT: Để đạt được chính xác những gì bạn đang tìm kiếm ở trên, bạn muốn sử dụng này để ghi đè các setter mặc định trong file mô hình của bạn:

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

Sau đó mã bạn có trên hoạt động.

Tôi quan tâm đến việc tìm hiểu thêm về nội bộ ActiveRecord và nền tảng lập trình meta không thể xuyên thấu của nó, vì vậy tôi đã cố gắng hoàn thành những gì bạn đã mô tả trong phần bình luận bên dưới. Dưới đây là một ví dụ mà làm việc cho tôi (đây là tất cả trong 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 

điều khiển đầu ra:

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> 

Trong cuộc sống thực tôi đoán bạn muốn include module trong ActiveRecord: : Cơ sở, trong một tệp ở đâu đó trước đó trong đường dẫn tải. Bạn cũng phải xử lý đúng kiểu đối số mà bạn đang chuyển sang hàm cơ sở dữ liệu. Cuối cùng, tôi đã biết rằng connection.execute được thực hiện bởi mỗi bộ điều hợp cơ sở dữ liệu, vì vậy cách bạn truy cập kết quả có thể khác trong Postgres (ví dụ này là SQLite3, trong đó tập kết quả được trả về dưới dạng mảng băm và khóa cho bản ghi dữ liệu đầu tiên . là 0]

bài viết trên blog này là vô cùng hữu ích:

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

như là Rails hướng dẫn cho plugin-authoring:

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

Ngoài ra, đối với những gì nó có giá trị, tôi nghĩ rằng trong Postgres tôi vẫn làm điều này bằng cách sử dụng một di chuyển để tạo ra một truy vấn viết lại quy tắc, nhưng điều này làm cho một kinh nghiệm học tập tuyệt vời. Hy vọng nó hoạt động và tôi có thể ngừng suy nghĩ về cách làm điều đó ngay bây giờ.

+1

Một cách hoàn toàn khác để thực hiện điều này mà tôi vừa nghĩ là sử dụng công cụ ghi lại truy vấn của Postgresql. Một chút giống như sử dụng một kích hoạt. Bạn chỉ cần thiết lập quy tắc để viết lại các cập nhật sao cho giá trị được truyền qua hàm. Nhược điểm là mô hình của bạn được sạch sẽ và bạn chỉ thực hiện một cuộc gọi đến cơ sở dữ liệu. Nhược điểm là nó không phải là cơ sở dữ liệu-thuyết bất khả tri, mà rõ ràng có thể không phải là một vấn đề trong trường hợp của bạn. Tôi không đủ quen thuộc với cú pháp để đăng ví dụ, nhưng đây là liên kết tới tài liệu: http://www.postgresql.org/docs/8.4/static/sql-createrule.html –

+0

Tôi cảm thấy như ở đây nên là một cách hiệu quả hơn để làm điều này liên quan đến khỉ vá ActiveRecord/Arel –

+0

Nếu bằng "hiệu quả", bạn có nghĩa là hiệu suất, phương pháp viết lại truy vấn là cấp trên; Tôi không thấy làm thế nào bạn có thể thực hiện bất cứ điều gì trong Rails mà sẽ tránh được hai cuộc gọi đến db. Nếu bằng "hiệu quả" nghĩa là DRYer/biểu cảm hơn (giả sử đây không phải là nơi duy nhất bạn muốn làm điều này), thì tôi đồng ý đó là một lựa chọn. –

Các vấn đề liên quan