Search code examples
ruby-on-railspostgresqlactiverecordjsonb

ruby on rails jsonb column default value


I have a model ProjectKeyword where I use jsonb datatype in the column :segemnted_data

class ProjectKeyword < ApplicationRecord
  belongs_to :project
  belongs_to :keyword
  has_many :project_keyword_dimensions
  has_many :dimensions, through: :project_keyword_dimensions

  validates :project_id, :keyword_id, presence: true
end

Migration

class AddSegemtnedDataToProjectKeywords < ActiveRecord::Migration[5.0]
  def change
    add_column :project_keywords, :segmented_data, :jsonb, default: '{}'
    add_index  :project_keywords, :segmented_data, using: :gin
  end
end

My problem is when I create new project_keyword instance the default value of the segmented_data is a string not a hash and I cannot update this field or merge with another hash For example

[12] pry(#)> new_pr_keyword = ProjectKeyword.new(project_id: 1671333, keyword_id: 39155)
=> #<ProjectKeyword:0x007fd997641090 id: nil, project_id: 1671333, keyword_id: 39155, segmented_data: "{}">
[13] pry(#)> new_pr_keyword.save!
=> true
[14] pry(#)> new_pr_keyword.segmented_data.update({'new_data' => 'some_data'})
NoMethodError: undefined method `update' for "{}":String
from (pry):14:in `block (3 levels) in <top (required)>'

But when I asign hash value to the field segmented_data before update then update method works fine.

For example

[15] pry(#)> new_pr_keyword.segmented_data = {'new_data' => 'some_data'}
=> {"new_data"=>"some_data"}
[16] pry(#)> new_pr_keyword.save!
=> true
[17] pry(#)> new_pr_keyword.segmented_data.update({'new_data_2' => 'some_data_2'})
=> {"new_data"=>"some_data", "new_data_2"=>"some_data_2"}
[18] pry(#)> new_pr_keyword.save!
=> true

The question is how to make default value of segmented_data to be a Hash class not a String so method update then will work straight away on this field, after object just was created.


Solution

  • This has worked for me on several projects:

    add_column :project_keywords, :segmented_data, :jsonb, default: {}
    

    (not a string, a ruby hash)

    I seem to recall this not working on Rails 3, but it should be fine in Rails 4.