I would like to limit the number of items in an association. I want to ensure the User doesn\'t have more than X Things. This question was asked before and the solution had th
In Rails 4
, perhaps earlier versions you can simply validate on the value of the counter_cache
.
class User
has_many :things
validates :things_count, numericality: { less_than: 5 }
end
class Thing
belongs_to :user, counter_cache: true
validates_associated :user
end
note that I've used :less_than
because :less_than_or_equal_to
would allow the things_count
to be 6
since it is validated after the counter cache update.
If you want to set a limit on a per user basis, you can create a things_limit
column to dynamically compare with the limit value you've set.
validates :things_count, numericality: { less_than: :things_limit }
I thought I'd chime in here. It seems like most of the answers here fail under race-conditions. I'm trying to limit the number of users who can sign up at a certain price point in our app. Checking the limit in Rails means that 10 simultaneous registrations could get through, even if it exceeds the limit I'm trying to set.
For example, say I want to restrict registrations to no more than 10. Let's say that I already have 5 users registered. Let's also say that 6 new users attempt to register at the same time. In 6 different threads, Rails reads the number of slots remaining, and it gets the answer 5
. This passes validation. Rails then allows all registrations to go through, and I have 11 registrations. :/
Here's how I solved this problem:
def reserve_slot(price_point)
num_updated = PricePoint.where(id: price_point.id)
.where('num_remaining <= max_enrollments')
.update_all('num_remaining = num_remaining + 1')
if num_updated == 0
raise ActiveRecord::Rollback
end
end
Using this approach, I never allow more registrations than max_enrollments
, even when the app is under load. This is because the validation and increment is done in a single, atomic database operation. Note, too that I always call this method from within a transaction, so it rolls back under failure.
try this, just like string:
class User < ActiveRecord::Base
has_many :things, :dependent => :destroy
validates :things, length: {maximum: 4}
end
So if you want a different limit for each user you can add things_limit:integer into User and do
class User
has_many :things
validates_each :things do |user, attr, value|
user.errors.add attr, "too much things for user" if user.things.size > user.things_limit
end
end
class Thing
belongs_to :user
validates_associated :user, :message => "You have already too much things."
end
with this code you can't update the user.things_limit to a number lower than all the things he already got, and of course it restrict the user to create things by his user.things_limit.
Application example Rails 4 :
https://github.com/senayar/user_things_limit
You should try this.
class Thing <ActiveRecord::Base
belongs_to :user
validate :thing_count, :on => :create
def thing_count
user = User.find(id)
errors.add(:base, "Exceeded thing limit") if user.things.count >= 5
end
end
You could try validates_length_of
and validates_associated
:
class Client < ActiveRecord::Base
has_many :orders
validates :orders, :length => { :maximum => 3 }
end
class Order < ActiveRecord::Base
belongs_to :client
validates_associated :client
end
A quick test shows that the valid?
method works as expected, but it does not stop you from adding new objects.