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, но прежде чем я спустился по этому маршруту, я хотел бы знать, есть ли способ сделать это уже.
Ответы
Ответ 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.