easily stop double posts

You have a comment form. Someone clicks twice. Oh noes! Double post!

think first

What’s the real problem with double posting? It’s not that the user clicked “submit” several times. The problem is that we have a duplicate record in our database. So instead of fixing this at the view level, we can solve it at the model level in a one-line validation:

class Comment < ActiveRecord::Base
  belongs_to :post
  validates_uniqueness_of :body, :scope => [:post_id, :name]


The above validation acts exactly like a multicolumn database constraint. The order of the fields doesn’t matter, because what gets checked is the uniqueness of the combination of fields (:body, :post_id, and :name). So if someone wants to comment “cool!” on every post, they can do it, but they can’t comment “cool!” twice on the same post.

Scoping is all the rage right now. But uniqueness scoping is not really related.

show me show me

This solution is implemented here, on Snax. Also, we rolled it into Chow not too long ago. I work on Chow these days. It’s sweet.

5 responses

  1. Databases can check uniqueness for you, which is arguably faster than doing it in Rails validation.

    add_index :posts, :body, :unique => true

    Of course you don’t get the convenience of validation messages, but this is a viable solution when your tables get very large.

  2. Ouch, that could result in a pretty big whack to the database, no? I can just see the SELECT * FROM posts WHERE content="*1 kilobyte of text*" queries right now…and that’s painful since no-one indexes their content for those sorts of searches.

    The quickest optimized solution I can think of, so far, would be to hash the content, store the hash, and then put uniqueness on that. That would add pretty little to the model, although would be something else to juggle, naturally.

  3. I don’t think that’s an issue. Isn’t it more like:

    SELECT COUNT FROM comments
    WHERE content = 'X' AND post_id = 3

    The id of the parent post is easily indexed and the query optimizer should take advantage of that. Also, the content comparison is just equality and not a LIKE or a GLOB. I think the write operation itself as well as updating all your caches will dwarf any penalty from the uniqueness select, even at the top level.

    I don’t think a hash buys you anything.