Don't use finders with validation helpers

Rather innocently, I tried using this validation on my Category model.

validates_exclusion_of :name,
                       :within => Widget.find(:all).collect(&:name), 
                       :message => "used by a widget"

The idea behind this validation was to make sure a Category doesn't have the same name as a Widget.

But since validations are class methods, they get loaded whenever you access a model. So that means we get an extra SQL query every time we do anything with our Category class, not just on create or update.

User Load (0.001142)   SELECT * FROM widgets LIMIT 1

This might not seem like a big deal, but it's always good practice to keep database calls to a minimum.

So what's the workaround? Use a protected #validate method instead.

def validate
  errors.add(:name, "used by a widget") if Widget.find_by_name(self.name)
end

You get the same validation, but without all the extra SQL love.

May 25, 2007

Comments

Jamie Hill May 25, 2007

Your first example, the query would only happen once in production mode since validates_exclusion_of is a class method. It would however only have the widgets that existed the first time your category model was used.

Patrick Crowley May 25, 2007

That's interesting, Jamie.

I only looked at this in development mode, but sounds like you'd still want to use my workaround to keep the list of names current.

New comments are disabled right now. Once we finish migrating this blog, we'll restore them.