73

Users can add tags to a snippet:

class Snippet < ActiveRecord::Base

  # Relationships
  has_many :taggings
  has_many :tags, :through => :taggings
  belongs_to :closing_reason

end

I want to validate the number of tags: at least 1, at most 6. How am I about to do this? Thanks.

3 Answers3

84

A better solution has been provided by @SooDesuNe on this SO post

validates :tags, length: { minimum: 1, maximum: 6 }
Community
  • 1
  • 1
sbonami
  • 1,892
  • 17
  • 33
  • 3
    FWIW, the default error message for this is "is too long (maximum is 100 characters)" – user605331 Jul 18 '17 at 14:23
  • 8
    If you want to add a custom error message: validates :tags, length: { minimum: 1, message: "are required. Please add at least one." } – Vassilis Oct 03 '18 at 20:10
  • 1
    An even nicer way for validation messages is to override the `too_long` validation message for that model's attribute in a locale (e. g. `en.activerecord.errors.models.user.attributes.tags.too_long`) so you don't have to handle the translations by hand in the model itself. – pdu Aug 19 '21 at 09:55
75

You can always create a custom validation.

Something like

  validate :validate_tags

  def validate_tags
    errors.add(:tags, "too much") if tags.size > 5
  end
Nick
  • 9,493
  • 8
  • 43
  • 66
Nikita Rybak
  • 67,365
  • 22
  • 157
  • 181
  • 13
    I would probably use `tags.count` so that the DB does the work and you don't load all the tags just for this one validation. – Peter Brown Jan 29 '11 at 15:11
  • 7
    @Beerlington: In fact it seems that Nikita is right, you don't want to hit the database for a validation, you want to check the actual length of "tags" of the object. – tokland Sep 29 '11 at 10:33
  • 31
    If you use tags.size, it does the "right" thing. see http://blog.hasmanythrough.com/2008/2/27/count-length-size – Matt Scilipoti Nov 16 '11 at 12:58
  • 1
    This stops working when you create a Tag that references an existing Snippet. What you should do is attach a `validate: true` to the `belongs_to` relationship. Not to mention Rails has built in length validation with `validates_length_of`. – maletor Jun 06 '14 at 00:13
  • 2
    I don't think this is thread-safe - is it? What happens if two workers try to insert tags? – Edward May 02 '15 at 18:20
  • validates_numericality_of :next_release, :less_than => 6 Just wanting to let the next reader know there's a better way. – baash05 Apr 08 '16 at 15:15
  • @baash05 the `length` validator is appropriate here, not the `numericality` validator. Tags respond to `length` and aren't a number explicitly. – sbonami Mar 14 '17 at 14:25
9

I think you can validate with using .reject(&:marked_for_destruction?).length.

How about this?

class User < ActiveRecord::Base
  has_many :groups do
    def length
      reject(&:marked_for_destruction?).length
    end
  end
  accepts_nested_attributes_for :groups, allow_destroy: true
  validates :groups, length: { maximum: 5 }
end

Or this.

class User < ActiveRecord::Base
  has_many :groups
  accepts_nested_attributes_for :groups, allow_destroy: true
  GROUPS_MAX_LENGTH = 5
  validate length_of_groups

  def length_of_groups
    groups_length = 0
    if groups.exists?
      groups_length = groups.reject(&:marked_for_destruction?).length
    end
    errors.add(:groups, 'too many') if groups_length > GROUPS_MAX_LENGTH
  end
end

Then, you can command.

@user.assign_attributes(params[:user])
@user.valid?

Thank you for reading.

References:

http://homeonrails.com/2012/10/validating-nested-associations-in-rails/ http://qiita.com/asukiaaa/items/4797ce44c3ba7bd7a51f

Ray Baxter
  • 3,181
  • 23
  • 27
asukiaaa
  • 1,594
  • 17
  • 19
  • 1
    I had no idea about the ability to pass a block to association methods. That is phenomenal, thank you. – Christian Bankester Oct 14 '15 at 15:11
  • Two years later: the `length` method in the block is actually never called. A block passed to `has_many` is actually called an extension. You can create new "finders, creators and other factory-type methods" with it. https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many – haffla Nov 02 '17 at 10:21