Accepts_nested_attributes_ для неудачной проверки дочерней ассоциации

Я использую accepts_nested_attributes_for в одной из моих моделей Rails, и я хочу сохранить дочерние элементы после создания родителя.

Форма работает отлично, но проверка не выполняется. Для простоты представьте себе следующее:

class Project < ActiveRecord::Base
  has_many :tasks
  accepts_nested_attributes_for :tasks
end

class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project_id
  validates_associated :project
end

И я бегу:

Project.create!(
  :name => 'Something',
  :task_attributes => [ { :name => '123' }, { :name => '456' } ]
)

При сохранении модели проекта проверка не выполняется в задачах, потому что у них нет project_id (так как проект не был сохранен).

Кажется, что Rails выполняет следующий шаблон:

  • Проверить проект
  • Подтвердить задачи
  • Сохранить проект
  • Сохранить задачи

Образец должен быть:

  • Проверить проект
  • On Pass: Сохранить проект и продолжить...
  • Подтвердить задачи
    • On Pass: Save Tasks
    • В случае сбоя: удалить проект (возможно, откат)

Итак, мой вопрос сводится к следующему: Как я могу получить Rails для запуска метода project_id = (или project =) и проверки для дочерних элементов (задач) ПОСЛЕ того, как родительский (проект) был сохранен, но НЕ сохраняет родителя ( проект), если какой-либо ребенок (задача) недействителен?

Любые идеи?

Ответ 1

Используйте этот ответ для Rails 2, иначе см. ниже ответ :inverse_of

Вы можете обойти это с помощью не проверки для project_id, если соответствующий проект действителен.


class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project_id, :unless => lambda {|task| task.project.try(:valid?)}
  validates_associated :project
end

Ответ 2

Используйте :inverse_of и validates_presence_of :parent. Это должно исправить вашу проблему проверки.

   class Dungeon < ActiveRecord::Base
     has_many :traps, :inverse_of => :dungeon
   end

   class Trap < ActiveRecord::Base
     belongs_to :dungeon, :inverse_of => :traps
     validates_presence_of :dungeon
   end

http://apidock.com/rails/ActiveModel/Validations/HelperMethods/validates_presence_of

https://github.com/rails/rails/blob/73f2d37505025a446bb5314a090f412d0fceb8ca/activerecord/test/cases/nested_attributes_test.rb

Ответ 3

Проверяйте только отношения, а не идентификатор:

class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project
end

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

class Task < ActiveRecord::Base
  belongs_to :project, :autosave => true

  validates_presence_of :project
end

Ответ 4

К сожалению, ни одно из приведенных выше предложений не работает для меня с Rails 2.3.5.

В моем случае проект в задаче всегда равен нулю, если оба они созданы с использованием вложенных атрибутов. Только если я удалю validates_presence_of, создание пройдет успешно. unit test и журнал показывают, что все создано правильно.

Итак, теперь я хотел бы добавить ограничения к БД вместо Rails, поскольку в первую очередь это кажется более надежным.

Ответ 5

Вы можете просто создать проект и добавить только проекты, если он пройдет проверку:

tasks = params.delete(:task_attributes)
if Project.create(params)
  Project.update_attributes(:task_attributes => tasks)
end

Чао

Ответ 6

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

Описанная вами проблема будет исправлена ​​в Rails 3.0. Я бы разместил ссылку на билет Lighthouse, но stackoverflow.com не разрешает это, потому что я новый пользователь (#fail). Но пока вы можете использовать плагин " parental_control", который исправит вашу "ошибку".