71 votes

accepts_nested_attributes_for child association validation failing

J'utilise accepts_nested_attributes_for dans un de mes modèles Rails, et je veux sauvegarder les enfants après avoir créé le parent.

Le formulaire fonctionne parfaitement, mais la validation échoue. Pour simplifier, imaginez ce qui suit :

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

Et je cours :

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

Lors de l'enregistrement du modèle de projet, la validation échoue sur les tâches car elles n'ont pas de project_id (puisque le projet n'a pas été enregistré).

Il semble que Rails suive le modèle ci-dessous :

  • Valider le projet
  • Valider les tâches
  • Sauver le projet
  • Sauvegarder les tâches

Le modèle devrait être :

  • Valider le projet
  • Sur Pass : Sauvegarder le projet et continuer...
  • Valider les tâches
    • On Pass : Sauvegarder les tâches
    • En cas d'échec : Supprimer le projet (retour en arrière peut-être ?)

Donc ma question se résume à : Comment puis-je faire en sorte que Rails exécute la méthode project_id= (ou project=) et la validation sur les enfants (tâches) APRÈS que le parent (projet) ait été sauvegardé, mais ne sauvegarde PAS le modèle parent (projet) si un enfant (tâche) est invalide ?

Des idées ?

161voto

boblin Points 1899

Utilice :inverse_of y validates_presence_of :parent . Cela devrait résoudre votre problème de validation.

   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

12voto

The Who Points 3724

Utilisez cette réponse pour Rails 2, sinon voir ci-dessous pour le :inverse_of réponse

Vous pouvez contourner ce problème en no vérification du project_id si le projet associé est valide.

class Task < ActiveRecord::Base
  belongs_to :project

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

9voto

Ne validez que la relation, pas l'ID :

class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project
end

Dès que l'association est remplie, ActiveRecord considère que la validation a réussi, que le modèle soit sauvegardé ou non. Vous pouvez également envisager la sauvegarde automatique, pour vous assurer que le projet de la tâche est toujours sauvegardé :

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

  validates_presence_of :project
end

2voto

Michael Reinsch Points 524

Malheureusement, aucune des suggestions ci-dessus ne fonctionne pour moi avec Rails 2.3.5.

Dans mon cas, le projet dans une tâche est toujours nul si les deux sont créés en utilisant des attributs imbriqués. Ce n'est que si je supprime le validates_presence_of que la création se déroule avec succès. Le test unitaire et le journal montrent que tout est créé correctement.

J'aurais donc tendance à ajouter des contraintes à la base de données plutôt qu'à Rails, car cela semble être plus fiable en premier lieu.

1voto

Vous pourriez simplement créer le projet et n'ajouter les projets que s'ils passent la validation :

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

Ciao

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X