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

Улучшен метод validates_associated для Rails 3?

Rails 3 включает validates_associated, который автоматически вызывается при сохранении вложенной модели. Проблема с методом - сообщение ужасное - "Модель недействительна"

Было несколько сообщений, которые атаковали эту проблему для Rails 2:

и, вероятно, больше. Было бы замечательно иметь лучшую версию, как описано в этих сообщениях, совместимых с Rails 3. Основное улучшение будет заключаться в том, чтобы указать, почему связанная с этим модель не работает.

4b9b3361

Ответ 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 - это все, что мне нужно.