Когато 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
Потърсете в източника на адаптер PostgreSQL за "regclass" и ще видите някои други заявки, които AR ще използва, за да разбере структурата на таблицата.
pg_get_exprкод>
call в горната заявка е мястото, откъдето идва стойността по подразбиране на колоната.
Резултатите от тази заявка отиват, повече или по-малко, направо в 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-ify по подразбиране; края на превключвателя
в 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 (като before_create
).
IMO би било много по-добре, ако AR остави стойностите по подразбиране до базата данни, ако не ги разбира:оставянето им извън INSERT или използването на DEFAULT в VALUES би дало много по-добри резултати; AR, разбира се, ще трябва да презареди новосъздадени обекти от базата данни, за да получи всички правилни настройки по подразбиране, но ще имате нужда от презареждане само ако има настройки по подразбиране, които AR не разбира. Ако друго
в extract_value_from_default
използва специален флаг „Не знам какво означава това“ вместо nil
тогава условието „Трябва да презаредя този обект след първото записване“ ще бъде тривиално за откриване и ще презаредите само когато е необходимо.
Горното е специфично за PostgreSQL, но процесът трябва да е подобен за други бази данни; обаче не давам гаранции.