Установить значение по умолчанию для столбца 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.


person Manuel Meurer    schedule 31.10.2013    source источник
comment
Вы пытались установить значение по умолчанию вручную внутри модели? AR обычно вырезает или игнорирует значения по умолчанию, которые не понимает.   -  person mu is too short    schedule 31.10.2013
comment
Да, прямо сейчас я использую обратный вызов after_initialize, но обычно мне это не нравится...   -  person Manuel Meurer    schedule 01.11.2013
comment
попробуйте "", я полагаю, что это то, что используется для hstore для обозначения пустого хэша   -  person John Bachir    schedule 11.12.2013
comment
Можете ли вы попробовать установить значение по умолчанию на ноль?   -  person tolgap    schedule 30.12.2013
comment
Ну, это null по умолчанию... по умолчанию. Я не хочу, чтобы это было null, я хочу, чтобы это было {}.   -  person Manuel Meurer    schedule 30.12.2013


Ответы (3)


Хотя немного поздно, это сработало для меня (требуется 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 три типа значений:

  1. Объекты

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

  2. Массивы

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

  3. Скаляры

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

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

=# SELECT * FROM foo WHERE bar->>1 = 'baz';
ERROR:  cannot extract element from a scalar
person janfoeh    schedule 23.12.2013
comment
Я получаю следующую ошибку: PG::UndefinedFunction: ERROR: function to_json(text) does not exist HINT: No function matches the given name and argument types. You might need to add explicit type casts. - person Manuel Meurer; 29.12.2013
comment
@ManuelMeurer Я считаю, что для этого вам нужна последняя версия Postgres; Я на 9.3.2. - person janfoeh; 29.12.2013
comment
Да, это было добавлено в 9.3. Не могли бы вы упомянуть об этом в своем ответе? Тогда я могу это принять. - person Manuel Meurer; 29.12.2013
comment
Еще одна маленькая вещь, вы можете изменить > на >=? Ваше здоровье! - person Manuel Meurer; 31.12.2013
comment
Этот синтаксис также работает: SET DEFAULT '{}'::JSON; Я нахожу его чище. - person Offirmo; 10.06.2014
comment
@Офирмо, спасибо! Ваш ответ на самом деле работает, а мой был немного нарушен. - person janfoeh; 17.09.2014
comment
@ManuelMeurer, если вы все еще работаете с типом данных JSON, вас может заинтересовать обновленный ответ. - person janfoeh; 17.09.2014

Код ниже работает для 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 = '{}';"

приложение.rb

config.active_record.schema_format = :sql
person Anton Orel    schedule 13.04.2014

Вы можете просто сделать что-то вроде этого

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

Укажите значение по умолчанию с помощью default: [] или default: {} или любым другим удобным для вас способом.

Обновление: обратите внимание, что это для Rails 4+

person Olalekan Sogunle    schedule 06.05.2020
comment
Как упоминалось в моем вопросе 6,5 лет назад, приложение все еще было на Rails 3, так что это не будет (не) работать. Не могли бы вы улучшить свой ответ, указав, из какой версии Rails работает ваше предложение? - person Manuel Meurer; 07.05.2020