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 the logic in the child:
class User < ActiveRecord::Base has_many :things, :dependent => :destroy end class Thing <ActiveRecord::Base belongs_to :user validate :thing_count_within_limit, :on => :create def thing_count_within_limit if self.user.things(:reload).count >= 5 errors.add(:base, "Exceeded thing limit") end end end
The hard coded "5" is an issue. My limit changes based on the parent. The collection of Things knows its limit relative to a User. In our case, a Manager may adjust the limit (of Things) for each User, so the User must limit its collection of Things. We could have thing_count_within_limit request the limit from its user:
if self.user.things(:reload).count >= self.user.thing_limit
But, that's a lot of user introspection from Thing. Multiple calls to user and, especially, that (:reload)
are red flags to me.
I thought has_many :things, :before_add => :limit_things
would work, but we must raise an exception to stop the chain. That forces me to update the things_controller to handle exceptions instead of the rails convention of if valid?
or if save
.
class User has_many :things, :before_add => limit_things private def limit_things if things.size >= thing_limit fail "Limited to #{thing_limit} things") end end end
To do this, I have to update the parent model, the child's controller, AND I can't follow convention? Am I missing something? Am I misusing has_many, :before_add
? I looked for an example using :before_add, but couldn't find any.
I thought about moving the validation to User, but that only occurs on User save/update. I don't see a way to use it to stop the addition of a Thing.
I prefer a solution for Rails 3 (if that matters for this problem).
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
Validating on the current count causes the count to become greater than the limit once the save has completed. The only way I've found way to prevent the create from occurring is to validate that before the create, the number of things is less than the limit.
This is not to say that it isn't useful having a validation on the count in the User model, but doing so doesn't prevent User.things.create
from being called because the user's count collection is valid until the new Thing object is saved, and then becomes invalid after the save.
class User has_many :things end class Thing belongs_to :user validate :on => :create do if user && user.things.length >= thing_limit errors.add(:user, :too_many_things) end end end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With