web-dev-qa-db-fra.com

Modèles imbriqués et validation parent

J'ai deux modèles. 
- Parent has_many Children;
- Parent includes_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

J'utilise validation pour valider la présence d'enfants pour chaque parent. Je ne peux donc pas sauver un parent sans enfants.

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

travaux de validation. Ensuite, nous allons détruire les enfants via l'attribut _destroy:

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

afin que je puisse détruire tous les enfants via des formulaires imbriqués et la validation passera.

En fait, cela se produit car après que j'ai supprimé l'enfant de mon parent via _delete, la méthode children retourne toujours l'objet détruit avant de le recharger. La validation a donc été transmise:

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

Est-ce un bug?

Quelle est la question. La question est la meilleure solution pour le réparer. Mon approche consiste à ajouter un filtre before_destroy à Child pour vérifier si c'est le dernier. Mais cela rend le système compliqué.

39
fl00r

Cela fonctionnera probablement pour vous, mais j’ai le sentiment que la réponse est bien meilleure. Cela ressemble à un bug pour moi.

class Parent
  validate :must_have_children

  def must_have_children
    if children.empty? or children.all? {|child| child.marked_for_destruction? }
      errors.add(:base, 'Must have at least one child')
    end
  end
end
59
John Douthat

Ce n'est pas un bug. Selon la documentation

Valide que les attributs Spécifiés ne sont pas vides (comme défini Par Objet # vierge?)

et validates :children, :presence => true est identique. La documentation ne dit pas ce qui se passe si vous essayez de l'utiliser sur une association. Vous devez utiliser la validation personnalisée à l'aide de validate.

Utiliser validates_presence_of sur has_many appelle l'association blank? sur l'association children, qui est un objet de la classe Array. Puisque le blank? n'est pas défini pour une Array, il déclenche method_missing qui est pris dans Rails. Habituellement, il fait ce que vous voulez mais j'ai trouvé qu'il échouait dans Rails 3.1rc et Ruby 1.8.7 d'une manière vraiment horrible: il annule en silence les modifications des enregistrements associés. Il m'a fallu quelques heures pour savoir ce qui se passait. 

0
Tomáš K.