Rails: Force empty string to NULL in the database

Solution 1:

Yes, the only option at the moment is to use a callback.

before_save :normalize_blank_values

def normalize_blank_values
  attributes.each do |column, value|
    self[column].present? || self[column] = nil
  end
end

You can convert the code into a mixin to easily include it in several models.

module NormalizeBlankValues
  extend ActiveSupport::Concern

  included do
    before_save :normalize_blank_values
  end

  def normalize_blank_values
    attributes.each do |column, value|
      self[column].present? || self[column] = nil
    end
  end

end

class User
  include NormalizeBlankValues
end

Or you can define it in ActiveRecord::Base to have it in all your models.

Finally, you can also include it in ActiveRecord::Base but enable it when required.

module NormalizeBlankValues
  extend ActiveSupport::Concern

  def normalize_blank_values
    attributes.each do |column, value|
      self[column].present? || self[column] = nil
    end
  end

  module ClassMethods
    def normalize_blank_values
      before_save :normalize_blank_values
    end
  end

end

ActiveRecord::Base.send(:include, NormalizeBlankValues)

class User
end

class Post
  normalize_blank_values

  # ...
end

Solution 2:

Try if this gem works:

https://github.com/rubiety/nilify_blanks

Provides a framework for saving incoming blank values as nil in the database in instances where you'd rather use DB NULL than simply a blank string...

In Rails when saving a model from a form and values are not provided by the user, an empty string is recorded to the database instead of a NULL as many would prefer (mixing blanks and NULLs can become confusing). This plugin allows you to specify a list of attributes (or exceptions from all the attributes) that will be converted to nil if they are blank before a model is saved.

Only attributes responding to blank? with a value of true will be converted to nil. Therefore, this does not work with integer fields with the value of 0, for example...

Solution 3:

Another option is to provide custom setters, instead of handling this in a hook. E.g.:

def foo=(val)
  super(val == "" ? nil : val)
end