Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rails has_many minimum collection size update validation

I have a has_many association that accepts nested attributes. I need for there to be a minimum of 1 associated object in the collection, so I wrote a custom validator:

class MinimumCollectionSizeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    if value.size < options[:size]
      record.errors[attribute] << (options[:message] || "must have at least #{options[:size]} line.")
    end
  end
end

The model looks like:

has_many :foos, :dependent=>:destroy
accepts_nested_attributes_for :foos
validates :foos, :minimum_collection_size=>{:size=>1}

This works great on model creation, but fails miserable on update. @my_model.update_attributes(params[:my_model]) returns true even if all the foos are removed by _destroy.

How do I get update_attributes to behave the same as save?

like image 841
SooDesuNe Avatar asked Dec 20 '10 03:12

SooDesuNe


3 Answers

A better way to validate the minimum size of a collection:

validates :my_association, :length => { :minimum => Fixnum}
like image 123
SooDesuNe Avatar answered Nov 06 '22 06:11

SooDesuNe


SooDesuNe

Yes, validates :foos, :length => {:minimum => 1, :message=>"At least one foo" } is better than the original one, but the same issue still happens.

To fix it, we could use validate method:

validate :validate_foos

private
def validate_foos
  remaining_foos = foos.reject(&:marked_for_destruction?)
  errors.add :foos, "At least one foo" if remaining_foos.empty?

Hope it helps to who encountered the same problem.

like image 21
aqingsao Avatar answered Nov 06 '22 06:11

aqingsao


Create validator:

class CollectionLengthValidator < ActiveModel::Validations::LengthValidator
  def validate_each(record, attribute, value)
    value = value.respond_to?(:reject) ? value.reject(&:marked_for_destruction?) : value
    super(record, attribute, value)
  end
end

This is resolve problem with delete last record in collection.

Then use in model as standard length validator:

validates :foos, :collection_length => {:minimum => 1}
like image 13
Serhiy Nazarov Avatar answered Nov 06 '22 08:11

Serhiy Nazarov