Улучшен метод validates_associated для Rails 3?
Rails 3 включает validates_associated
, который автоматически вызывается при сохранении вложенной модели. Проблема с методом - сообщение ужасное - "Модель недействительна"
Было несколько сообщений, которые атаковали эту проблему для Rails 2:
и, вероятно, больше. Было бы замечательно иметь лучшую версию, как описано в этих сообщениях, совместимых с Rails 3. Основное улучшение будет заключаться в том, чтобы указать, почему связанная с этим модель не работает.
Ответы
Ответ 1
В отношении отношения вы можете использовать :autosave => true
вместо этого, который попытается сохранить модели детей при сохранении родителя. Это автоматически запустит проверки дочерних элементов, и они сообщают с соответствующими сообщениями об ошибках.
Кроме того, если вы добавите проверку на присутствие дочернего элемента, чтобы родительский объект был установлен, и вы создаете дочерние объекты через ассоциацию, вам даже не нужен флаг autosave
, и вы получите красивое сообщение об ошибке, Например:
class Trip < ActiveRecord::Base
validates :name, :presence => true
attr_accessible :name
has_many :places, dependent: :destroy, :inverse_of => :trip
end
class Place < ActiveRecord::Base
belongs_to :trip
validates :name, :trip, presence: true
attr_accessible :name
end
Затем вы можете получить хорошее сообщение об ошибке со следующим сценарием использования:
> trip = Trip.new(name: "California")
=> #<Trip id: nil, name: "California">
> trip.places.build
=> #<Place id: nil, name: nil, trip_id: nil>
> trip.valid?
=> false
> trip.errors
=> #<ActiveModel::Errors:0x00000004d36518 @base=#<Trip id: nil, name: "California">, @messages={:places=>["is invalid"]}>
> trip.errors[:places]
=> ["is invalid"]
Я думаю, что validates_associated
- это религия эпохи перед автосохранением детей, и это не лучший способ сделать что-то еще. Конечно, это не обязательно хорошо документировано. Я не уверен на 100%, что это относится и к Rails 2.3, но я чувствую, что это так. Эти изменения произошли, когда была добавлена функция вложенных атрибутов (которая была когда-то в 2.x).
Это упрощенный фрагмент кода из проекта который я опубликовал в github.
Ответ 2
У меня была эта проблема, и в конце я использовал решение, данное здесь Ben Lee:
проверяет, связано ли сообщение об ошибке модели
Бен говорит:
Вы можете написать свой собственный валидатор, основанный на коде для встроенного валидатора.
Поднимая исходный код для validates_associated, мы видим, что он использует "AssociatedValidator". Исходный код для этого:
module ActiveRecord
module Validations
class AssociatedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all?
record.errors.add(attribute, :invalid, options.merge(:value => value))
end
end
module ClassMethods
def validates_associated(*attr_names)
validates_with AssociatedValidator, _merge_attributes(attr_names)
end
end
end
end
Таким образом, вы можете использовать это в качестве примера для создания настраиваемого валидатора, который генерирует сообщения об ошибках следующим образом:
module ActiveRecord
module Validations
class AssociatedBubblingValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
(value.is_a?(Array) ? value : [value]).each do |v|
unless v.valid?
v.errors.full_messages.each do |msg|
record.errors.add(attribute, msg, options.merge(:value => value))
end
end
end
end
end
module ClassMethods
def validates_associated_bubbling(*attr_names)
validates_with AssociatedBubblingValidator, _merge_attributes(attr_names)
end
end
end
end
Вы можете поместить этот код в инициализатор, что-то вроде /initializers/associated_bubbling_validator.rb
.
Наконец, вы бы подтвердили так:
class User < ActiveRecord::Base
validates_associated_bubbling :account
end
ПРИМЕЧАНИЕ: вышеуказанный код полностью не протестирован, но если он не работает напрямую, мы надеемся достаточно, чтобы поставить вас на правильный трек
Ответ 3
validates_associated запускает проверки, указанные в соответствующем классе объектов. Ошибки на уровне родительского класса просто говорят "мой ребенок недействителен". Если вам нужны детали, выведите ошибки на дочернем объекте (на уровне дочерней формы в представлении).
Ответ 4
В большинстве случаев validates_existence_of - это все, что мне нужно.