sql >> Databáze >  >> RDS >> PostgreSQL

Jak mohu získat přístup k výchozí hodnotě sloupce Postgres pomocí ActiveRecord?

Když ActiveRecord potřebuje vědět o tabulce, provede dotaz podobný vašemu informačnímu schématu dotaz, ale AR projde přes Systémové tabulky specifické pro PostgreSQL místo toho:

  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

Prohledejte zdroj adaptéru PostgreSQL pro "regclass" a uvidíte některé další dotazy, které AR použije k určení struktury tabulky.

pg_get_expr volání ve výše uvedeném dotazu je místo, odkud pochází výchozí hodnota sloupce.

Výsledky tohoto dotazu jsou víceméně přímo 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

Kód PostgreSQLColumn konstruktor bude používat extract_value_from_default k Ruby-ify výchozí; konec přepínač v extract_value_from_default je zajímavé zde:

else
  # Anything else is blank, some user type, or some function
  # and we can't know the value of that, so return nil.
  nil

Pokud je tedy výchozí hodnota vázána na sekvenci (což je id sloupec v PostgreSQL bude), pak výchozí vyjde z databáze jako volání funkce podobné tomuto:

nextval('models_id_seq'::regclass)

To skončí ve výše uvedeném else větev a column.default.nil? bude pravda.

Pro id to není problém, AR očekává, že databáze dodá hodnoty pro id sloupců, takže je jedno, jaká je výchozí hodnota.

To je velký problém, pokud je výchozí nastavení sloupce něco, čemu AR nerozumí, řekněme volání funkce jako jako md5(random()::text) . Problém je v tom, že AR inicializuje všechny atributy na jejich výchozí hodnoty – jako Model.columns vidí je, ne tak, jak je vidí databáze – když řeknete Model.new . Například v konzole uvidíte věci jako toto:

 > Model.new
=> #<Model id: nil, def_is_function: nil, def_is_zero: 0>

Pokud tedy def_is_function ve skutečnosti používá jako výchozí hodnotu volání funkce, AR to bude ignorovat a pokusí se vložit NULL jako hodnotu tohoto sloupce. Tato NULL zabrání použití výchozí hodnoty a skončíte s matoucím nepořádkem. Výchozí hodnoty, kterým AR rozumí (jako jsou řetězce a čísla), však fungují dobře.

Výsledkem je, že ve skutečnosti nemůžete použít netriviální výchozí hodnoty sloupců s ActiveRecord, pokud chcete netriviální hodnotu, musíte to udělat v Ruby pomocí jednoho ze zpětných volání ActiveRecord (například before_create ).

IMO by bylo mnohem lepší, kdyby AR ponechal výchozí hodnoty na databázi, pokud jim nerozuměla:vynechat je z INSERT nebo použít DEFAULT v VALUES by přineslo mnohem lepší výsledky; AR by samozřejmě musel znovu načíst nově vytvořené objekty z databáze, aby získal všechny správné výchozí hodnoty, ale nové načtení byste potřebovali pouze v případě, že by existovaly výchozí hodnoty, kterým AR nerozuměl. Pokud else v extract_value_from_default místo nil použil speciální příznak „Nevím, co to znamená“. pak by bylo triviální detekovat podmínku "Po prvním uložení musím tento objekt znovu načíst" a znovu načtete pouze v případě potřeby.

Výše uvedené je specifické pro PostgreSQL, ale proces by měl být podobný pro ostatní databáze; nicméně neposkytuji žádné záruky.




  1. MySQL vs MongoDB 1000 přečtení

  2. Načítání 5 milionů řádků do Pandas z MySQL

  3. Po odeslání formuláře pomocí php a jquery ajax nelze vložit data do databáze

  4. Připojte nebo připojte objekt json do jiného objektu json Objekt json pomocí plsql