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')"
%>