ActiveRecordがテーブルについて知る必要がある場合、ActiveRecordはクエリと同様のクエリを実行しますが、ARは代わりにPostgreSQL固有のシステムテーブルinformation_schema
を通過します。
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アダプタソースで「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
PostgreSQLColumn
コンストラクターはextract_value_from_default
、デフォルトをRuby化するために使用します。inの終わりは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
したがって、デフォルト値がシーケンス(id
PostgreSQLの列にバインドされる)にバインドされている場合、デフォルトは次のような関数呼び出しとしてデータベースから出力されます。
nextval('models_id_seq'::regclass)
それは上記のelse
ブランチに行き着き、column.default.nil?
真実になります。
列の場合、id
これは問題ではありません。ARはデータベースがid
列の値を提供することを期待しているため、デフォルト値が何であるかは関係ありません。
列のデフォルトがARが理解できないものである場合、これは大きな問題です。たとえば、などの関数呼び出しmd5(random()::text)
です。問題は、あなたが言うと、ARがすべての属性をデフォルト値に初期化することです-Model.columns
データベースがそれらを見るようにではなく、それらを見るように- Model.new
。たとえば、コンソールには次のようなものが表示されます。
> Model.new
=> #<Model id: nil, def_is_function: nil, def_is_zero: 0>
したがって、def_is_function
実際にデフォルト値として関数呼び出しを使用する場合、ARはそれを無視し、その列の値としてNULLを挿入しようとします。そのNULLは、デフォルト値が使用されるのを防ぎ、混乱を招く混乱を招きます。ただし、ARが理解できるデフォルト(文字列や数値など)は問題なく機能します。
その結果、ActiveRecordで重要なデフォルトの列値を実際に使用することはできません。重要な値が必要な場合は、RubyでActiveRecordコールバックの1つ(などbefore_create
)を使用する必要があります。
IMOは、ARがデフォルト値を理解していない場合、それらをデータベースに任せた方がはるかに優れています。INSERTから除外するか、VALUESでDEFAULTを使用すると、はるかに優れた結果が得られます。もちろん、ARは、すべての適切なデフォルトを取得するために、データベースから新しく作成されたオブジェクトをリロードする必要がありますが、ARが理解できないデフォルトがあった場合にのみ、リロードが必要になります。else
でextract_value_from_default
代わりに特別な「これが何を意味するのかわからない」フラグを使用した場合、nil
「最初の保存後にこのオブジェクトをリロードする必要があります」という条件を検出するのは簡単で、必要な場合にのみリロードします。
上記はPostgreSQL固有ですが、プロセスは他のデータベースでも同様である必要があります。ただし、保証はいたしかねます。