在Rails < 4中为Postgres JSON列设置默认值

17

我开始使用Postgres的JSON数据类型,现在有很多有趣的东西可以用它实现。 在我的一个Rails应用程序中,该应用程序还不是Rails 4(在Rails 4中已经添加了对Postgres JSON的支持),我通过以下方式添加了一个JSON列:

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

但是我无法弄清如何为该列设置默认值。 我尝试了所有变化,例如{}'{}''{}'::json'[]'::json等,但无论哪种方式,迁移运行时要么出现错误,要么根本不起作用,这意味着当我创建新的Foo时,barnil


1
你尝试过在模型内手动设置默认值吗?AR通常会破坏或忽略它不理解的默认值。 - mu is too short
是的,现在我使用 after_initialize 回调,但我通常不喜欢这些... - Manuel Meurer
尝试使用 "",我相信这是用于表示空哈希的 hstore 的常用方法。 - John Bachir
1
rake中止! 发生错误,此及所有后续迁移都已取消: PG :: InvalidTextRepresentation:ERROR:类型json的输入语法无效 详细信息:输入字符串意外结束。 上下文:JSON数据,第1行: :ALTER TABLE“foo”ADD COLUMN“bar”json DEFAULT''``` - Manuel Meurer
你能试着将默认值设置为“null”吗? - tolgap
默认情况下,它是null... 默认情况下。我不希望它是null,我希望它是{} - Manuel Meurer
3个回答

31

虽然有点晚,但这对我有用(需要Postgres >= 9.3):

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

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

编辑:这个答案曾经主张使用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

1
我遇到了以下错误: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. - Manuel Meurer
@ManuelMeurer 我相信你需要一个较新的Postgres版本;我用的是9.3.2。 - janfoeh
1
是的,它在9.3中添加了。你能在你的答案中提到吗?那我就可以接受它了。 - Manuel Meurer
还有一件小事,你能把 > 改成 >= 吗?谢谢! - Manuel Meurer
4
这个语法也可以使用:SET DEFAULT '{}'::JSON; 我认为更加简洁。 - Offirmo
显示剩余2条评论

5

以下代码适用于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

0

你基本上可以像这样做

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

使用 default: []default: {} 或其他你感到舒适的任何东西来声明默认值。

更新:请注意,这是针对 Rails 4+ 的。


正如我在6.5年前的问题中提到的那样,该应用程序仍在使用Rails 3,因此这不起作用(没有起作用)。您能否改进您的答案,说明从哪个版本的Rails开始您的建议适用? - Manuel Meurer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接