Kiedy ActiveRecord potrzebuje wiedzieć o tabeli, wykonuje zapytanie podobne do twojego schemat_informacji
zapytanie, ale AR przejdzie przez Tabele systemowe specyficzne dla PostgreSQL
zamiast tego:
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
Wyszukaj źródło adaptera PostgreSQL dla "regclass", a zobaczysz kilka innych zapytań, których AR użyje do ustalenia struktury tabeli.
pg_get_expr
wywołanie w powyższym zapytaniu to miejsce, z którego pochodzi domyślna wartość kolumny.
Wyniki tego zapytania idą mniej więcej prosto do 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
Kolumna PostgreSQL
konstruktor
użyje extract_value_from_default
do Ruby-ify domyślnie; koniec przełącznik
w extract_value_from_default
jest tu interesujące:
else
# Anything else is blank, some user type, or some function
# and we can't know the value of that, so return nil.
nil
Więc jeśli domyślna wartość jest powiązana z sekwencją (którą jest id
kolumna w PostgreSQL), wtedy domyślnie wyjdzie z bazy danych jako wywołanie funkcji podobne do tego:
nextval('models_id_seq'::regclass)
To skończy się w powyższym innym
branch i kolumna.default.nil?
będzie prawdą.
Dla id
kolumna to nie jest problem, AR oczekuje, że baza danych dostarczy wartości dla id
kolumn, więc nie obchodzi go, jaka jest wartość domyślna.
Jest to duży problem, jeśli domyślną wartością kolumny jest coś, czego AR nie rozumie, powiedz wywołanie takiej funkcji jako md5(random()::text)
. Problem polega na tym, że AR zainicjuje wszystkie atrybuty do ich wartości domyślnych – jako Model.columns
widzi je, a nie tak, jak widzi je baza danych – kiedy mówisz Model.new
. Na przykład w konsoli zobaczysz takie rzeczy:
> Model.new
=> #<Model id: nil, def_is_function: nil, def_is_zero: 0>
Więc jeśli def_is_function
faktycznie używa wywołania funkcji jako wartości domyślnej, AR zignoruje to i spróbuje wstawić NULL jako wartość tej kolumny. Ta wartość NULL zapobiegnie użyciu wartości domyślnej i skończysz z mylącym bałaganem. Domyślne wartości, które AR może zrozumieć (takie jak ciągi i liczby) działają dobrze.
Rezultat jest taki, że tak naprawdę nie możesz używać nietrywialnych domyślnych wartości kolumn z ActiveRecord, jeśli chcesz nietrywialną wartość, musisz to zrobić w Rubim za pomocą jednego z wywołań zwrotnych ActiveRecord (takich jak before_create
).
IMO byłoby znacznie lepiej, gdyby AR pozostawił wartości domyślne w bazie danych, jeśli ich nie rozumiał:pozostawienie ich poza WSTAWKĄ lub użycie DOMYŚLNYCH w WARTOŚCIACH dałoby znacznie lepsze wyniki; AR musiałby oczywiście ponownie załadować nowo utworzone obiekty z bazy danych, aby uzyskać wszystkie prawidłowe wartości domyślne, ale ponowne wczytanie byłoby potrzebne tylko wtedy, gdy istnieją wartości domyślne, których AR nie rozumie. Jeśli inny
w extract_value_from_default
użył specjalnej flagi „Nie wiem, co to znaczy” zamiast nil
wtedy warunek „Muszę ponownie załadować ten obiekt po pierwszym zapisaniu” byłby trywialny do wykrycia i ładowałbyś go ponownie tylko wtedy, gdy jest to konieczne.
Powyższe jest specyficzne dla PostgreSQL, ale proces powinien być podobny dla innych baz danych; jednak nie udzielam żadnych gwarancji.