2010-11-30 14 views
10

Comment puis-je faire en sorte qu'au moins deux enregistrements d'options sont requis pour soumettre un produit?Rails attributs imbriqués: nécessitent au moins deux enregistrements

class Product < ActiveRecord::Base 
    belongs_to :user 
    has_many :options, :dependent => :destroy 
    accepts_nested_attributes_for :options, :allow_destroy => :true, :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } } 
    validates_presence_of :user_id, :created_at 
    validates :description, :presence => true, :length => {:minimum => 0, :maximum => 500} 
end 

class Option < ActiveRecord::Base 
    belongs_to :product 
    validates :name, :length => {:minimum => 0, :maximum => 60}     
end 
+0

Il devrait être assez simple avec une validation personnalisée. Quelque chose comme 'self.errors.add_to_base (" Deux options sont obligatoires ") sauf self.options.length> = 2' – Todd

+0

merci qui a fonctionné! – morcutt

+0

Si vous utilisez 'accept_nested_attributes_for' avec' allow_destroy: true' alors vous devez utiliser 'marked_for_destruction?' Avec l'association enfants pour trouver la longueur exacte des enfants, car il peut être possible lors de la soumission de forme certains objets ont été marqués '_destroy: true' pour la destruction après avoir sauvé l'objet. La longueur, la taille et le nombre ne fonctionneront pas parfaitement dans ce cas. Ce lien a une réponse parfaite. [link] (http://stackoverflow.com/a/28476834/4377172) –

Répondre

15
class Product < ActiveRecord::Base 
    #... all your other stuff 
    validate :require_two_options 

    private 
    def require_two_options 
     errors.add(:base, "You must provide at least two options") if options.size < 2 
    end 
end 
+1

add_to_base (msg) a été abandonné, utilisez Errors # add (: base, msg) à la place – AnApprentice

+0

Modifié, merci pour le conseil – karmajunkie

+4

options.count va générer une requête SQL COUNT pour trouver le nombre d'options que vous avez. Si vos options sont en mémoire et ne sont pas enregistrées dans la base de données, cela donnera une réponse inattendue car elles ne seront pas incluses dans le compte. [Dans ce cas, envisagez d'utiliser la taille.] (Http://stackoverflow.com/questions/6083219/activerecord-size-vs-count) –

12

Juste une considération au sujet karmajunkie Réponse: J'utiliser size au lieu de count parce que si certains ont été construits (et non enregistrée) objet imbriqué comporte des erreurs, il ne serait pas considéré (encore son pas sur la base de données) .

class Product < ActiveRecord::Base 
    #... all your other stuff 
    validate :require_two_options 

    private 
    def require_two_options 
     errors.add(:base, "You must provide at least two options") if options.size < 2 
    end 
end 
+0

.size est le chemin à parcourir comme vous l'avez dit, même si ce n'est pas dans la base de données. – wallerjake

+0

'.count' n'a pas fonctionné pour moi non plus. '.size' est définitivement le chemin à parcourir. – Tintin81

1

Si votre formulaire permet des enregistrements à supprimer alors .size ne fonctionnera pas car il comprend les enregistrements marqués pour la destruction.

Ma solution a été:

validate :require_two_options 

private 
def require_two_options 
    i = 0 
    product_options.each do |option| 
     i += 1 unless option.marked_for_destruction? 
    end 
    errors.add(:base, "You must provide at least two option") if i < 2 
end 
+0

+1 Bon point sur les enregistrements d'attention qui sont marqués pour la destruction. Cependant, une façon plus simple d'obtenir 'i' pourrait être' i = product_options.reject {| option | option.marked_for_destruction? } .size'. – februaryInk

0

Code tidier, testé avec Rails 5:

class Product < ActiveRecord::Base 
    OPTIONS_SIZE_MIN = 2 
    validate :require_two_options 

    private 

    def options_count_valid? 
    options.reject(&:marked_for_destruction?).size >= OPTIONS_SIZE_MIN 
    end 

    def require_two_options 
    errors.add(:base, 'You must provide at least two options') unless options_count_valid? 
    end 
end