Home > Software engineering >  After changing a column's datatype and default value, ActiveRecord still uses the old default v
After changing a column's datatype and default value, ActiveRecord still uses the old default v

Time:08-06

We had the table configurations with column preferences of type jsbon and default {}.

Initial migration:

  def up
    add_column :configurations, :preferences, :jsonb
    change_column_default :configurations, :preferences, "{}"
  end

  def down
    remove_column :configurations, :preferences
  end

We later decided to change this column's datatype to text, array: true and default: [].

Because this table had no entries in our production servers, we dropped the column, created it again with a different datatype and added the new default value, all in the same migration:

Second migration:

  def up
    safety_assured {
      remove_column :configurations, :preferences
      add_column :configurations, :preferences, :text, array: true
      change_column_default :configurations, :preferences, []
    }
  end

  def down
    safety_assured {
      remove_column :configurations, :preferences
      add_column :configurations, :preferences, :jsonb
      change_column_default :configurations, :preferences, "{}"
    }
  end

schema.rb after migration:

t.text "preferences", default: [], array: true

Now, when we create a new entry on the configurations table using the web interface and going through the controller, preferences is populated with the old {} default value.

Controller action:

  def create
    @configurations = association.build_configuration
  end

To make things even more strange, using the server console to build the record instead, ActiveRecord uses the right default []:

Console:

Association.build_configuration

=> #<Configuration id: nil, association_id: 1, created_at: nil, updated_at: nil, preferences: []>

Why is the ActiveRecord build method using different defaults when used in the controller vs using it via console?

Things we've tried:

  • Restarting the server after deploy
  • Running Configuration.connection.schema_cache.clear!
  • Running Configuration.reset_column_information

EDIT:

This is what Configuration._default_attributes returns:

"preferences"=>
    #<ActiveModel::Attribute::FromDatabase:0x000055988ccd99f0
     @name="preferences",
     @original_attribute=nil,
     @type=
      #<ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array:0x000055988cce8d38
       @delimiter=",",
       @pg_decoder=#<PG::TextDecoder::Array:0x000055988cce89f0 "text[]"  elements_type=nil needs quotation>,
       @pg_encoder=#<PG::TextEncoder::Array:0x000055988cce8bd0 "text[]"  elements_type=nil needs quotation>,
       @subtype=#<ActiveRecord::Type::Text:0x000055988cdef380 @limit=nil, @precision=nil, @scale=nil>>,
     @value_before_type_cast="{}"> 

CodePudding user response:

Solution:

Arrays in Postgres use curly braces, which is why it's showing up that way. Active Record is deserializing it to a Ruby array in the example above.

https://github.com/ankane/blazer/issues/407

  • Related