Вложенные модели и родительская проверка - PullRequest
40 голосов
/ 28 февраля 2011

У меня есть две модели.
- Parent has_many Children;
- Parent accept_nested_attributes_for Children;

class Parent < ActiveRecord::Base
  has_many :children, :dependent => :destroy
  accepts_nested_attributes_for :children, :allow_destroy => true
  validates :children, :presence => true
end

class Child < ActiveRecord::Base
  belongs_to :parent
end

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

parent = Parent.new :name => "Jose"
parent.save
#=> false
parent.children_attributes = [{:name => "Pedro"}, {:name => "Emmy"}]
parent.save
#=> true

валидационные работы. Тогда мы уничтожим детей с помощью атрибута _destroy:

parent.children_attributes = {"0" => {:id => 0, :_destroy => true}}
parent.save
#=> true !!!
parent.reload.children
#=> []

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

На самом деле это происходит потому, что после того, как я удаляю child из своего родителя с помощью _delete, метод children все еще возвращает уничтоженный объект, прежде чем я его перезагружаю, поэтому проверка прошла:

parent.children_attributes = {"0" => {:id => 0, :_destroy => true}}
parent.save
#=> true !!!
parent.children
#=> #<Child id:1 ...> # It's actually deleted
parent.reload.children
#=> []

Это ошибка?

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

Ответы [ 2 ]

60 голосов
/ 28 февраля 2011

Это, вероятно, сработает для вас, но я чувствую, что есть гораздо лучший ответ. Для меня это звучит как ошибка.

class Parent < ActiveRecord::Base
  validate :must_have_children

  def must_have_children
    if children.empty? || children.all?(&:marked_for_destruction?)
      errors.add(:base, 'Must have at least one child')
    end
  end
end
0 голосов
/ 31 мая 2011

Это не ошибка. Согласно документации

Подтверждает, что указанный атрибуты не являются пустыми (как определено по объекту # пусто?)

и validates :children, :presence => true - это то же самое. В документации не говорится, что произойдет, если вы попытаетесь использовать его в ассоциации. Вы должны использовать пользовательскую проверку, используя validate.

Использование validates_presence_of в has_many вызове ассоциации blank? в ассоциации children, которая является объектом класса Array. Поскольку blank? не определен для Array, он запускает method_missing, который перехватывается внутри Rails. Обычно он делает то, что вы хотите, но я обнаружил, что в Rails 3.1rc и Ruby 1.8.7 это действительно ужасно: он молча отменяет изменения связанных записей. Мне потребовалось пару часов, чтобы выяснить, что происходит.

...