gpt4 book ai didi

ruby-on-rails - 如何使用 ActiveRecord 访问 Postgres 列默认值?

转载 作者:行者123 更新时间:2023-11-29 11:28:11 26 4
gpt4 key购买 nike

我正在尝试访问我的 Postgres 9.2 数据库中列的默认值。通过使用原始 SQL,我可以验证列默认值是否为“users_next_id()”:

> db = ActiveRecord::Base.connection
> db.execute("SELECT table_name,column_name,column_default
FROM information_schema.columns
WHERE table_name = 'users' and column_name ='id'").first

=> {"table_name"=>"users",
"column_name"=>"id",
"column_default"=>"users_next_id()"}

但是当我使用 AR 的“列”方法时,默认值似乎是 nil:

[26] pry(main)> db.columns('users')[0]=> #<ActiveRecord::ConnectionAdapters::PostgreSQLColumn:0x007feb397ba6e8
@coder=nil,
@default=nil,
@limit=8,
@name="id",
@null=false,
@precision=nil,
@primary=nil,
@scale=nil,
@sql_type="bigint",
@type=:integer>

这不会造成任何问题(除了让我感到困惑),但这是预期的行为吗?我是否对“列”方法做出了错误的假设?

最佳答案

当 ActiveRecord 需要了解一个表时,它会执行类似于您的 information_schema 查询的查询,但 AR 将通过 PostgreSQL-specific system tables相反:

  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum

搜索 PostgreSQL adapter source对于“regclass”,您会看到 AR 将用来确定表结构的其他一些查询。

pg_get_expr上述查询中的调用是列默认值的来源。

该查询的结果或多或少是 straight into PostgreSQLColumn.new :

def columns(table_name, name = nil)
# Limit, precision, and scale are all handled by the superclass.
column_definitions(table_name).collect do |column_name, type, default, notnull|
PostgreSQLColumn.new(column_name, default, type, notnull == 'f')
end
end

PostgreSQLColumn constructor将使用 extract_value_from_default对默认值进行 Ruby 化; end of the switch extract_value_from_default 在这里很有趣:

else
# Anything else is blank, some user type, or some function
# and we can't know the value of that, so return nil.
nil

因此,如果默认值绑定(bind)到一个序列(PostgreSQL 中的 id 列),那么默认值将作为类似于以下的函数调用从数据库中出来:

nextval('models_id_seq'::regclass)

这将在上面的 else 分支中结束,并且 column.default.nil? 将为真。

对于 id 列这不是问题,AR 希望数据库为 id 列提供值,因此它不关心默认值是什么.

如果列的默认值是 AR 不理解的东西,这是一个大问题,say a function call such as md5(random()::text) .问题是 AR 会将所有属性初始化为它们的默认值——当你说 Model.new 时,Model.columns 看到它们,而不是数据库看到它们。例如,在控制台中您会看到如下内容:

 > Model.new
=> #<Model id: nil, def_is_function: nil, def_is_zero: 0>

因此,如果 def_is_function 实际上使用函数调用作为其默认值,AR 将忽略它并尝试插入 NULL 作为该列的值。该 NULL 将阻止使用默认值,您最终会陷入困惑。不过,AR 可以理解的默认设置(例如字符串和数字)工作得很好。

结果是你不能真正在 ActiveRecord 中使用非平凡的默认列值,如果你想要一个非平凡的值,那么你必须在 Ruby 中通过 ActiveRecord 回调之一(例如 before_create )。

IMO 如果 AR 在不理解默认值的情况下将默认值留给数据库会更好:将它们留在 INSERT 之外或在 VALUES 中使用 DEFAULT 会产生更好的结果;当然,AR 必须从数据库中重新加载新创建的对象才能获得所有正确的默认值,但只有在存在 AR 不理解的默认值时才需要重新加载。如果 extract_value_from_default 中的 else 使用特殊的“我不知道这是什么意思”标志而不是 nil 那么“我需要重新加载第一次保存后的这个对象”条件很容易检测到,您只会在必要时重新加载。


以上是 PostgreSQL 特有的,但其他数据库的过程应该类似;但是,我不作任何保证。

关于ruby-on-rails - 如何使用 ActiveRecord 访问 Postgres 列默认值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15236497/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com