How to stop a user submitting the same data more than once.

I have a standard form built with rails, which a user to my site can
use to submit data. Unfortunately one user just managed to submit exactly the the same data three times in a row.

From the server logs it seems as though he didn't use his back button and then resubmit everything. It looks more like he got a bit fed up waiting for the form to process and pressed the submit button three times.

if the back action just fetched the page from the browser's local
cache you wouldn't be able to tell.

However, I didn't think this was possible in Rails. Is this correct?

Nothing particular in rails to stop this (or rather it is a client
side problem really - rails just sees 3 inbound requests)

Fred

I have just finished entering the same data into IE and was rather surprised to see that I could submit it many times. So, it seems to be the case that Firefox 3.5 surpresses this behaviour, whilst IE lets you submit your data as many times as you want.

Do you think it would be possible to stop this by using a session.

In PHP I would do something like this:

session_start(); if (!session_is_registered("counted")){ $query= insert_database(); session_register("counted");

That won't stop it because there is a window in between where you check and when you set the session variable (ie a race condition).

Fred

They could have also hit the 'enter' key multiple times too, not clicking anything.

But in either case, a small amount of javascript will prevent this.

Cheers for the reply Fred. So how would one go about stopping this? Surely I cannot be the only one with this problem?

enter just processes the submit button. if you disable the submit button, then enter key press will be disabled as well. personally, i would get rid of the submit tag and use a image_submit_tag to get rid of stupid keyboard annoyances. This will force the user to click on the link to submit and you can do things like asking for confirmation or disabling it once pressed.

Thanks for all of the answers. I solved it a little differently and will explain how in case a) it helps anybody else b) there is a flaw in my method which I have overlooked

What I did was in the controller: Create a model object: @applicant = Applicant.new(params[:applicant]) Wait for a post request. Look up the last email address sucessfully stored in the database. Check if it is the same as the email address currently being submitted?

If so, it is possible that someone has pressed submit twice, or used the back button in their browser. This can therefore be ignored and the user redirected to the success view.

If the email adresses are not identical, check if @applicant can be saved. If it can, redirect to success, if it can't re render index view.

If the applicant is redirected to the success view without their data being saved (i.e. if both email addresses are the same), then they also receive a flash message that a double data entry was detected for their email address and that the record was only saved once.

For this particular application it is not desired that people can apply twice and in practice it rarely happens.

Good to hear that you've solved the problem yourself. Is now too late to point out that you could probably use validates_uniqueness_of in your model? http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002167

I have a similar problem that I'm working on, but I was going to take a slightly different approach.

My problem is that I'm collecting completely anonymous data at first, but then I want people to be able to come in later and create an account, then hopefully be able to find their records. I'm not sure if this scenario is even realistic, but here is where I am at in my thinking so far…

The user enters the initial data and their IP address is stored along with it (could this be my main downfall? I need to do more research on how these work and if they are truly unique for each user). When they come back (hopefully on the same computer!) and either try to enter the data again or go straight to making an account, I want to find a matching IP and validate that they are a returning user from the Model… then basically ask them if they've been here before, if so yada yada yada…

This is still turning around in my brain and I have yet to do any research on it…but if anyone see's any flaws in this method, feel free to let me know.

Jim - it seems that if you can do your validation in the Model that would be better practice, but I'm not sure if that suits your needs.

If you dont want duplicate data then just add the following to the model. Thats where validation should be.

validates_uniqueness_of :email_address

Thanks for the replies.

I'm also not sure if doing the validation in the model would work. Assuming that the user has a slow connection and hits submit on the form twice, then the first time the email would be unique, so everything would be good. However, would it not be the case that the second time the user presses submit, the record would have been stored, but the success screen would have not been displayed (hence them feeling the need to press submit a second time). In this case the email address would no longer be unique and an error would be thrown. So, potentially the user could think that their data had not been submitted, when in fact what they would be seeing was an error caused by their second submission (their first having been successful).

Nonetheless, thank you very much for the link Aldric. It is definitely useful to understand what is happening behind the scenes in Active Record, and it elaborates on the race conditions Fred mentioned. I will have a play around with validates_uniqueness_of this evening.

After talking to some friends of mine, the IP address is NOT a good idea...looks like I'll be looking at a cookie solution instead.

And maybe one more.. Again, I know you dislike javascript, but.. :slight_smile:

This isn't exactly generic - I'm pulling it straight out of something I'm doing right now. Why not show a spinner (and disable the submit button?) when they press submit and hide the spinner when that's done ? It's all about the feedback, right? :slight_smile:

<%= image_tag("spinner.gif",             :align => "absmiddle",             :border => 0,             :id => "spinner",             :style =>"display: none;" ) %> </center>

<%= observe_field :date, # The field to observe             :with => :date, # The input to validate #:on => "onselect", # What action to check. With JS updates, use frequency. :frequency => 1, # The frequency in seconds to watch for changes :url => {:action => 'filter_widget', :controller => 'dictated_exams',           :method => :post },         # The action to call when changes occur :update => :filter_counts, # Name of the <div> to update :before => "Element.show('spinner')", :success => "Element.hide('spinner')" %>