2010-11-30 5 views
10

Jak mogę to zrobić, aby do przesłania produktu potrzebne były co najmniej dwa rekordy opcji?Atrybuty zagnieżdżone w Railsach: wymagają co najmniej dwóch rekordów.

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

Powinno być dość proste do zrobienia z walidacji niestandardowej. Coś w stylu 'self.errors.add_to_base (" Wymagane są dwie opcje "), chyba że self.options.length> = 2' – Todd

+0

dziękuję, że zadziałało! – morcutt

+0

Jeśli używasz 'accepts_nested_attributes_for' z' allow_destroy: true', musisz użyć 'marked_for_destruction?' Z powiązaniem children aby znaleźć dokładną długość dzieci, ponieważ może to być możliwe podczas przesyłania z formularza niektóre obiekty zostały oznaczone '_destroy: true' do zniszczenia po zapisaniu obiektu. Długość, rozmiar i liczba nie będą działać idealnie w tym przypadku. Ten link ma idealną odpowiedź. [link] (http://stackoverflow.com/a/28476834/4377172) –

Odpowiedz

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) został przestarzały, użyj błędów # add (: base, msg) zamiast tego – AnApprentice

+0

Edytowane, dzięki za podpowiedź – karmajunkie

+4

options.count wygeneruje zapytanie SQL COUNT, aby znaleźć liczbę dostępnych opcji. Jeśli twoje opcje znajdują się w pamięci i nie są zapisane w bazie danych, spowoduje to nieoczekiwaną odpowiedź, ponieważ nie zostaną uwzględnione w liczniku. [W takich przypadkach należy rozważyć użycie rozmiaru.] (Http://stackoverflow.com/questions/6083219/activerecord-size-vs-count) –

12

Wystarczy rozważyć o karmajunkie odpowiedź: użyłbym size zamiast count bo jeśli niektóre zbudowany (a nie zapisane) zagnieżdżony obiekt ma błędów, to nie można uznać (jego nie na bazie jeszcze) .

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 jest sposobem, w jaki można to zrobić, nawet jeśli nie ma go w bazie danych. – wallerjake

+0

'.count' również nie działa dla mnie. '.size' to zdecydowanie droga. – Tintin81

1

Jeśli formularz pozwala rekordy mają być usunięte, a następnie .size nie będzie działać, ponieważ zawiera rekordy zaznaczone do zniszczenia.

Moje rozwiązanie było:

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 Dobry punkt o rekrutowaniu rekordów oznaczonych jako zniszczone. Jednak nieco czystszym sposobem na uzyskanie 'i' może być' i = product_options.reject {| option | option.marked_for_destruction? } .size'. – februaryInk

0

porządniej kod, przetestowany z 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