Rails validates_uniqueness_of is completely broken

If you are using the Rails implementation of validates_uniqueness_of in your model to ensure duplicate data doesn’t get into your database, your application is broken. If you don’t use the database to ensure uniqueness with a key, then your app will fail at some point in the future. Probably just when you start getting some decent traffic and would like the app to not fail.

How do I know this? It happened to me this last weekend.

In checking this out, I find that Michael Koziarski of the Rails core team recently wrote

validates_uniqueness_of gives a nice error message, and does an ok job at guaranteeing uniqueness. Validates uniqueness of + a unique index does both.

In my app, does a nice job isn’t really good enough when it comes to uniqueness.

Michael has also posted that, hey, if you don’t like it, fix it and submit a patch, which is a great idea, but for this case is a really hard problem to solve for the general case across many databases.

I didn’t find a lot of specific fixes with code that I liked, so here’s how I fixed it in my app.

For my case, I already had a do some stuff and save the model method. In this method, I added some code (I stripped out my app specific code in the example).

  DUPLICATE_ERROR_MESSAGES = [
      "Duplicate entry"
  ]

  def save_new
    begin
      save    
    rescue ActiveRecord::StatementInvalid => error
      if DUPLICATE_ERROR_MESSAGES.any? { |msg| error.message =~ /#{Regexp.escape(msg)}/ }
        logger.info "Duplicate Entry exception from DB"
        errors.add_to_base('Duplicate item not allow')
        return false
      else
        raise
      end
    end
  end

I also added a unique key to the table to ensure the MySQL database throws the exception that the above code relies on.

I still keep the validates_uniqueness_of call, as this does give a more specific error message, but now I don’t rely on it to enforce uniqueness.

Shh, don’t tell DHH that I’m putting integrity checks into my database.

This entry was posted on Tue, 11 Dec 2007 04:59:43 GMT . You can follow any any response to this entry through the Atom feed. You can leave a comment or a trackback from your own site.
Tags


Trackbacks

Use the following link to trackback from your own site:
http://blog.craz8.com/trackbacks?article_id=rails-validates_uniqueness_of-is-completely-broken&day=10&month=12&year=2007

Comments

Leave a response

  1. Henrik N about 1 month later:

    I believe you could change

    if DUPLICATE_ERROR_MESSAGES.any? { |msg| error.message =~ /#{Regexp.escape(msg)}/ }

    to

    if error.message =~ Regexp.union(*DUPLICATE_ERROR_MESSAGES)

Leave a comment