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-IFY的默認值;在extract_value_from_default
的end of the switch
是這裏有意思:
else
# Anything else is blank, some user type, or some function
# and we can't know the value of that, so return nil.
nil
因此,如果默認值綁定到一個序列(在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.columns
看到它們,而不是數據庫看到它們 - 當您說Model.new
時。例如,在控制檯,你會看到這樣的事情:
> Model.new
=> #<Model id: nil, def_is_function: nil, def_is_zero: 0>
所以,如果def_is_function
實際使用函數調用作爲它的默認值,AR將忽略並嘗試插入一個NULL作爲該列的值。 NULL會阻止使用默認值,最終會導致混亂。 AR可以理解的默認值(如字符串和數字)工作得很好。
其結果是,如果您需要一個非平凡的值,那麼您必須通過ActiveRecord回調之一(例如before_create
)在Ruby中執行,您無法真正在ActiveRecord中使用不平凡的默認列值。
IMO它會好得多,如果AR如果不理解它們,則將缺省值留給數據庫:將它們從INSERT中刪除或在VALUES中使用DEFAULT會產生更好的結果;當然,AR會從數據庫中重新加載新創建的對象,以便獲得所有正確的默認值,但是如果存在AR不瞭解的默認值,則只需要重新加載。如果在extract_value_from_default
中的else
使用了一個特殊的「我不知道這意味着什麼」標誌而不是nil
,那麼「我需要在第一次保存後重新加載此對象」條件將很容易檢測到,並且只有在必要時纔會重新加載。
以上是PostgreSQL特有的,但其他數據庫的過程應該是類似的;但是,我沒有保證。
**簡短答案**:AR不知道'users_next_id()'意味着什麼,所以它假裝它不存在;對'id'列有特殊的處理,所以'columns'中的不正確的默認值不會引起麻煩(但只在'id'的特定情況下)。 **長答案**:這個遲到了,現在就跑。 – 2013-03-06 00:33:46
感謝您的信息。我之前一定是做錯了什麼,因爲現在我看到了其他列的默認值。你的回答很有道理。我認爲ID可能很特別,謝謝您的確認,這讓我再次檢查我的數據。 (感謝upvote - 我們noobs需要我們可以得到的所有鼓勵:) :) – MothOnMars 2013-03-06 00:50:37
這是一個很好的問題,因爲ActiveRecord在這裏的行爲很難直觀。像Rails一樣,找出WTF的唯一方法就是通過Rails源代碼進行瀏覽,這是一個相當先進的主題。嘿,每個人都以小菜作爲開始。 – 2013-03-06 04:34:05