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é.
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
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.