Maximum number of associated records
I'd like to have a maximum number of associated records on a model. E.g. a project has_many tasks, but not more then twenty.
How can I enforce this rule?
The only solution that I've been able to come up with so far is an
INSERT INTO...SELECT
query like this:
INSERT INTO
tasks (`id`,`project_id`,`title`,`body`)
SELECT
NULL, ?, ?, ?
FROM
tasks
HAVING
count(id) < MAX_NUMBER_OF_TASKS
LIMIT 1;
- As far as I can tell, this will guarantee a maximum number of tasks being inserted. Am I correct in this?
- Is there a 'Rails way' to do this?
- Is it possible to override ActiveRecord/the Task model so that it uses the query above to insert a new record?
I'm curre开发者_JAVA技巧ntly using a custom method with ActiveRecord::Base.connection
and calling that instead of .create
or .save
when new_record? == true
.
I haven't been able to try this, but I can't see why it shouldn't work.
Step one: Define a validator on the parent object (this is a simple implementation - could/should be made more generic):
class Project < ActiveRecord::Base
validate :max_tasks
def max_tasks
if tasks.count > 20
errors.add_to_base("Should not have more than 20 tasks")
end
end
end
Step two: Turn on validation of project from tasks:
class Task < ActiveRecord::Base
validates_associated :project
end
And I think you should be in business. When you try and save a new task, it'll validate the associated project, and the validation will fail if there are (now) more than 20 tasks associated.
Just in case you fancy making this more generic, you could do something like:
class NumberOfAssociatedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if options[:maximum] && record.send(attribute).count > options[:maximum]
record.errors[attribute] << "must not have more than #{options[:maximum]}"
end
if options[:minimum] && record.send(attribute).count < options[:minimum]
record.errors[attribute] << "must not have less than #{options[:minimum]}"
end
end
end
class MyModel < ActiveRecord::Base
validates :my_association, :number_of_associated => {:maxiumum => 20}
end
May be you can add some pre save validation to your model, that checks how many associated models it already have, and throw a validation error if it exceeds your max number of associations.
精彩评论