web-dev-qa-db-fra.com

échec de la validation de l'association d'enfant acceptes_nested_attributes_for

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

Le formulaire fonctionne parfaitement, mais la validation échoue. Par souci de simplicité, 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 d'id_projet (puisque le projet n'a pas été enregistré).

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

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

Le modèle doit être:

  • Valider le projet
  • On Pass: Enregistrer le projet et continuer ...
  • Valider les tâches
    • En passe: enregistrer les tâches
    • En cas d'échec: supprimer le projet (restauration peut-être?)

Donc ma question se résume à: Comment puis-je obtenir Rails pour exécuter la méthode project_id = (ou project =) et la validation sur les enfants (tâches) APRÈS que le parent (projet) a été enregistré, mais PAS enregistrer le modèle parent (projet) si un enfant (tâche) n'est pas valide?

Des idées?

71
Ryan Townsend

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

Vous pouvez contourner cela en pas en vérifiant le 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
12
The Who

Utilisation :inverse_of et 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

161
boblin

Validez uniquement 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érera que la validation a réussi, que le modèle soit enregistré ou non. Vous pouvez également étudier la sauvegarde automatique pour vous assurer que le projet de la tâche est toujours enregistré:

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

  validates_presence_of :project
end
9

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 à l'aide d'attributs imbriqués. Ce n'est que si je supprime validates_presence_of que la création se déroule correctement. Le test unitaire et le journal montrent que tout est créé correctement.

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

2
Michael Reinsch

Vous pouvez simplement créer le projet et ajouter les projets uniquement s'il passe la validation:

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

Ciao

1
user119264

Contrairement à ce que bigo suggère, il n'est pas toujours acceptable d'enregistrer d'abord l'objet parent puis les enfants. Habituellement, vous voulez vous assurer que tous les objets sont validés avant de commencer à les enregistrer. Cela donne à l'utilisateur la possibilité de rééditer le formulaire de saisie et de corriger les erreurs.

Le problème que vous décrivez sera résolu dans Rails 3.0. J'aurais publié un lien vers le ticket Lighthouse, mais stackoverflow.com ne le permet pas car je suis un nouvel utilisateur (#fail) Mais pour le moment, vous pouvez utiliser le plugin " parental_control ", qui corrigera votre "bug".

0
Thomas Watson