acceptpts_nested_attributes_ для проверки дочерней ассоциации не удалось - PullRequest
71 голосов
/ 01 июня 2009

Я использую acceptpts_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' } ]
)

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

Кажется, что Rails следует шаблону ниже:

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

Шаблон должен быть:

  • Подтвердить проект
  • В пути: сохранить проект и продолжить ...
  • Проверка задач
    • На проходе: сохранение задач
    • В случае ошибки: удалить проект (может быть откат?)

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

Есть идеи?

Ответы [ 6 ]

161 голосов
/ 24 января 2011

Используйте :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

12 голосов
/ 16 сентября 2009

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

Вы можете обойти это, не проверяя идентификатор проекта, если связанный проект действителен.


class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project_id, :unless => lambda {|task| task.project.try(:valid?)}
  validates_associated :project
end
9 голосов
/ 10 марта 2010

Подтвердите только связь, а не ID:

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
2 голосов
/ 09 марта 2010

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

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

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

1 голос
/ 08 июня 2009

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

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

Ciao

0 голосов
/ 03 августа 2009

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

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

...