Установите значение по умолчанию для столбца Postgres JSON в Rails <4

Итак, я начинаю использовать тип данных Postgres JSON, теперь там много интересного, что вы можете с ним сделать. В одном из моих приложений Rails, еще не Rails 4 (где была добавлена ​​поддержка Postgres JSON) Я добавил столбец JSON следующим образом:

create_table :foo do |t|
  t.column :bar, :json
end

но я не могу понять, как установить значение по умолчанию для столбца. Я пробовал все варианты, такие как {}, '{}', '{}'::json, '[]'::json и т.д., Но я либо получаю сообщение об ошибке при выполнении миграции, либо просто не работает, что означает, что выполняется миграция, но когда я создаю новый Foo, bar равен nil.

Ответ 1

Хотя немного поздно, это сработало для меня (требуется Postgres >= 9.3):

create_table :foo do |t|
  t.column :bar, :json
end

execute "ALTER TABLE foo ALTER COLUMN bar SET DEFAULT '[]'::JSON"

EDIT: этот ответ использовался для to_json('[]'::text) вместо '[]'::JSON - @Offirmo для подсказки.

Проблема со старым методом заключалась в том, что он фактически не определял массив или объект как значение по умолчанию, как можно было бы ожидать, а скаляр (строка), который был похож на один. Почему это имеет значение?

Postgres позволяет вставлять три типа значений в столбцы JSON:

  • Объекты

    INSERT INTO foo (bar) VALUE('{}')

  • Массивы

    INSERT INTO foo (bar) VALUE('[]')

  • Скаляры

    INSERT INTO foo (bar) VALUE('"string"')

Проблема заключается в том, что если вы смешиваете эти три вида в одном столбце, вы теряете возможность использовать JSON-операторы. Если вы установите значение по умолчанию "[]" с использованием ранее защищенного метода и запросили для элемента массива, столкновение с одной строкой со скалярным значением по умолчанию приведет к прерванию всего запроса с ошибкой:

=# SELECT * FROM foo WHERE bar->>1 = 'baz';
ERROR:  cannot extract element from a scalar

Ответ 2

Ниже приведен код ниже для PostgreSQL 9.3.4 и Rails 3.2.17

class YourModel < ActiveRecord::Base
...
  serialize :your_column, JSON
  before_create do
    self.your_column ||= {}
  end
...
end

код миграции

add_column :your_table, :your_column, :json
execute "ALTER TABLE your_table ALTER COLUMN your_column SET DEFAULT '{}'"
execute "UPDATE your_table SET your_column = '{}';"

application.rb

config.active_record.schema_format = :sql