Mot*_*ars 5 postgresql activerecord ruby-on-rails rails-activerecord
我正在尝试访问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()"}
Run Code Online (Sandbox Code Playgroud)
但是当我使用AR的'columns'方法时,默认值似乎是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>
Run Code Online (Sandbox Code Playgroud)
这不会导致任何问题(除了让我困惑),但这是预期的行为吗?我对"列"方法做出了错误的假设吗?
当ActiveRecord需要了解一个表时,它会执行类似于您的information_schema查询的查询,但AR将通过PostgreSQL特定的系统表来代替:
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
Run Code Online (Sandbox Code Playgroud)
在PostgreSQL适配器源中搜索"regclass",您将看到AR将用于确定表结构的其他一些查询.
pg_get_expr上述查询中的调用是列的默认值来自的位置.
该查询的结果或多或少直接进入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
Run Code Online (Sandbox Code Playgroud)
该PostgreSQLColumn构造函数将使用extract_value_from_default到Ruby-IFY的默认值; in 的结尾switchextract_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
Run Code Online (Sandbox Code Playgroud)
因此,如果默认值绑定到一个序列(idPostgreSQL 中的一列将是),那么默认值将作为函数调用从数据库中出现,类似于:
nextval('models_id_seq'::regclass)
Run Code Online (Sandbox Code Playgroud)
这将最终成为上述else分支,column.default.nil?并将成为现实.
对于id列而言这不是问题,AR期望数据库提供id列的值,因此它不关心默认值是什么.
如果列的默认值是AR不理解的东西,比如函数调用md5(random()::text),这是一个很大的问题.问题是,Model.columns当你说时,AR会将所有属性初始化为默认值 - 就像看到它们一样,而不是数据库看到它们Model.new.例如,在控制台中,您将看到如下内容:
> Model.new
=> #<Model id: nil, def_is_function: nil, def_is_zero: 0>
Run Code Online (Sandbox Code Playgroud)
因此,如果def_is_function实际使用函数调用作为其默认值,AR将忽略它并尝试插入NULL作为该列的值.那个NULL将阻止使用默认值,你最终会陷入混乱的混乱.AR可以理解的默认值(例如字符串和数字)工作得很好.
结果是你不能真正使用ActiveRecord的非平凡的默认列值,如果你想要一个非平凡的值,那么你必须通过一个ActiveRecord回调(例如before_create)在Ruby中做.
如果AR不理解它们,如果AR将默认值保留到数据库中会更好:将它们从INSERT中删除或在VALUES中使用DEFAULT会产生更好的结果; 当然,AR必须从数据库重新加载新创建的对象才能获得所有正确的默认值,但是如果存在AR不理解的默认值,则只需要重新加载.如果elsein中extract_value_from_default使用了一个特殊的"我不知道这意味着什么"的标志,而不是nil"我需要在第一次保存后重新加载这个对象"的条件,这将是微不足道的检测,你只需要在必要时重新加载.
以上是PostgreSQL特有的,但其他数据库的过程应该类似; 但是,我不保证.