2010-11-30 13 views
10

¿Cómo puedo hacer para que al menos dos registros de opciones sean necesarios para enviar un producto?Rails atributos anidados: requieren al menos dos registros

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

debería ser bastante fácil de hacer con una validación personalizada. Algo como 'self.errors.add_to_base (" Se requieren dos opciones ") a menos que self.options.length> = 2' – Todd

+0

¡gracias, funcionó! – morcutt

+0

Si está utilizando 'accepts_nested_attributes_for' con' allow_destroy: true' entonces debe usar 'marked_for_destruction?' Con la asociación de niños para encontrar la longitud exacta de los niños, porque puede ser posible al enviar desde el formulario algunos de los objetos han sido marcados como '_destroy: true' para la destrucción después de salvar el objeto. La longitud, el tamaño y el recuento no serán perfectos para ese caso. Este enlace tiene una respuesta perfecta. [link] (http://stackoverflow.com/a/28476834/4377172) –

Respuesta

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) ha quedado en desuso, use Errors # add (: base, msg) en su lugar – AnApprentice

+0

Editado, gracias por la sugerencia – karmajunkie

+4

options.count generará una consulta SQL COUNT para encontrar la cantidad de opciones que tiene. Si sus opciones están en la memoria y no se guardan en la base de datos, esto dará una respuesta inesperada ya que no se incluirán en el recuento. [En casos como este, considere usar tamaño.] (Http://stackoverflow.com/questions/6083219/activerecord-size-vs-count) –

12

Sólo una consideración sobre la respuesta karmajunkie: Me gustaría utilizar size en lugar de count porque si algunas de ellas construidas (y no guardado) objeto anidado tiene errores, que no se consideraría (no se encuentra en la base de datos aún) .

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 es el camino a seguir, ya que declaró que lo considera incluso si no está en la base de datos. – wallerjake

+0

'.count' tampoco funcionó para mí. '.size' es definitivamente el camino a seguir. – Tintin81

1

Si su formulario permite a los registros que se eliminarán después .size no funcionará, ya que incluye los registros marcados para su destrucción.

Mi solución fue:

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 Buen punto acerca de importar registros marcados para destrucción. Sin embargo, una forma algo más limpia de obtener 'i' podría ser' i = opciones_producto.reject {| option | option.marked_for_destruction? } .size'. – februaryInk

0

más ordenado código, prueba con los carriles 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 
Cuestiones relacionadas