【问题标题】:How to make attribute setter send value through SQL function如何使属性设置器通过 SQL 函数发送值
【发布时间】:2012-04-27 02:23:25
【问题描述】:

我试图让 ActiveRecord 模型中的属性设置器在 rails 生成其 sql 查询之前将其值包装在 text2ltree() postgres 函数中。

例如,

post.path = "1.2.3"
post.save

应该生成类似的东西

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

最好的方法是什么?

【问题讨论】:

  • 你可以写一个 Postgres 函数(存储过程)然后调用SELECT myfunc('1.2.3')。如果您对该路线感兴趣,我可以提供一个示例。

标签: sql ruby-on-rails postgresql activerecord arel


【解决方案1】:

编辑:要完全实现您在上面寻找的内容,您可以使用它来覆盖模型文件中的默认设置器:

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

那么你上面的代码就可以了。

我有兴趣更多地了解 ActiveRecord 的内部结构及其难以理解的元编程基础,因此作为练习,我尝试完成您在下面的 cmets 中描述的内容。这是一个对我有用的例子(这都在 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 中我仍然会使用迁移来创建查询重写规则,但这带来了很好的学习体验。希望它有效,我现在可以停止思考如何去做。

【讨论】:

  • 我刚刚想到的一种完全不同的方法是使用 Postgresql 的查询重写引擎。有点像使用触发器。您只需设置规则以重写更新,以便值通过函数传递。好处是您的模型是干净的,您只需调用一次数据库。缺点是它与数据库无关,在您的情况下这显然不是问题。我对发布示例的语法不够熟悉,但这里有一个文档链接:postgresql.org/docs/8.4/static/sql-createrule.html
  • 我觉得这应该是一种更有效的方法,涉及猴子修补 ActiveRecord/Arel
  • 如果“高效”是指性能,则查询重写方法更优越;我看不出如何在 Rails 中实现任何可以避免两次调用数据库的东西。如果你所说的“高效”是指更干燥/更有表现力(假设这不是你想要这样做的唯一地方),那么我同意这是一个选择。
  • 是的,我想我的意思更多的是第二个。这个问题更多地与了解 ActiveRecord/Arel 的内部结构有关。不过,我赞成您的回答,因为您确实提供了有价值的信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-17
  • 1970-01-01
  • 2017-06-04
  • 1970-01-01
  • 2022-12-09
相关资源
最近更新 更多