Подтвердить что ты не робот

Rails: принудительно введите пустую строку в NULL в базе данных

Есть ли простой способ (т.е. конфигурация), чтобы заставить ActiveRecord сохранять пустые строки как NULL в БД (если разрешен столбец)?

Причиной этого является то, что если в базе данных нет столбца строки NULLable без значения по умолчанию, новые записи, которые не устанавливают это значение, будут содержать NULL, тогда как новые записи, которые устанавливают это значение в пустую строку, не будут NULL, что приводит к несоответствиям в базе данных, которые я бы хотел избежать.

Сейчас я делаю такие вещи в своих моделях:

before_save :set_nil

def set_nil
  [:foo, :bar].each do |att|
    self[att] = nil if self[att].blank?
  end
end

который работает, но не очень эффективен или сух. Я мог бы включить это в метод и смешать его с ActiveRecord, но прежде чем я спустился по этому маршруту, я хотел бы знать, есть ли способ сделать это уже.

4b9b3361

Ответ 1

Попробуйте, если этот камень работает:

https://github.com/rubiety/nilify_blanks

Предоставляет структуру для сохранения входящих пустых значений как нуль в базе данных в случаях, когда вы предпочитаете использовать DB NULL, чем просто пустую строку...

В Rails при сохранении модели из формы и значения не предоставляются пользователем, пустая строка записывается в базу данных вместо NULL, как многие предпочитают (смешивание пробелов и NULL может запутаться). Этот плагин позволяет вам указать список атрибутов (или исключений из всех атрибутов), которые будут преобразованы в нуль, если они пусты, пока не будет сохранена модель.

Только атрибуты, ответившие на пробел? со значением true будет преобразован в nil. Поэтому это не работает с целыми полями со значением 0, например...

Ответ 2

Да, единственный вариант на данный момент - использовать обратный вызов.

before_save :normalize_blank_values

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

Вы можете преобразовать код в mixin, чтобы легко включить его в несколько моделей.

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

Или вы можете определить его в ActiveRecord:: Base, чтобы иметь его во всех своих моделях.

Наконец, вы также можете включить его в ActiveRecord:: Base, но включите его, когда потребуется.

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

Ответ 3

Мое предложение:

# app/models/contact_message.rb
class ContactMessage < ActiveRecord::Base
  include CommonValidations
  include Shared::Normalizer
end


# app/models/concerns/shared/normalizer.rb
module Shared::Normalizer
  extend ActiveSupport::Concern

  included do
    before_save :nilify_blanks
  end

  def nilify_blanks
    attributes.each do |column, value|
      # ugly but work
      # self[column] = nil if !self[column].present? && self[column] != false

      # best way
      #
      self[column] = nil if self[column].kind_of? String and self[column].empty?
    end
  end

end

Ответ 4

Другим вариантом является предоставление настраиваемых решений, вместо того, чтобы обрабатывать это с помощью крючка. Например:.

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

Ответ 5

Извините за некропостинг, но я не нашел здесь точной информации, если вам нужно решение для указать поля, которые необходимо очистить:

module EnforceNil
  extend ActiveSupport::Concern

  module ClassMethods
    def enforce_nil(*args)
      self.class_eval do
        define_method(:enforce_nil) do
          args.each do |argument|
            field=self.send(argument)
            self.send("#{argument}=", nil)  if field.blank?
          end
        end           
        before_save :enforce_nil
      end
    end
  end
end

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

Таким образом:

class User
  enforce_nil :phone #,:is_hobbit, etc  
end

Применение определенного поля удобно, если вы говорите, что у вас есть поле1 и поле2. Field1 имеет уникальный индекс в SQL, но может быть пустым, поэтому вам нужно принудительное выполнение (NULL считается уникальным, "не по SQL" ), но для field2 вам действительно неинтересно, и у вас уже есть десятки обратных вызовов или методов, которые работают когда поле2 является "", но выкопает ваше приложение под слоем ошибок, если поле2 равно nil. Ситуация, с которой я столкнулся.

Может быть полезно кому-то.

Ответ 6

Я использую атрибут normalizer для нормализации атрибутов до того, как они попадут в db.