Rails: preventing duplicate inserts due to clicking the back button and saving again

Think of a simple Rails scaffold application with a “new” action containing a form for adding records to the database using the Save button. After the "create" action, the controller is redirected to the "show" action, where the user can use the "edit" link to edit the record just inserted. So far so simple.

But if the user instead uses the "Back back" button after creating the entry to return to the "new" action, the browser will display a form with the values ​​that the user just entered. Now it changes some values ​​and clicks “save” again. He thinks this will change the record, but of course it creates a new record.

What is the preferred way to prevent duplicate entries? I am looking for a general solution, perhaps based on cookies or JavaScript.

+8
browser ruby-on-rails crud controller
source share
8 answers

After some research, I found a suitable cookie based solution. Here he is:

In the new "controller" of the controller, a time stamp with the current time is created and displayed as a hidden field. When the user submits the form, this timestamp returns to the create action of the controller. After creating an entry, this timestamp is stored in the session cookie. If the user returns to the "new" form using the "Back back" button, he gets an outdated form, which means that its timestamp is older than the one stored in the cookie. This is checked before creating the record and results in an error message.

Here is the controller code:

def new @post = Post.new @stale_form_check_timestamp = Time.now.to_i end def create @post = Post.new(params[:post]) if session[:last_created_at].to_i > params[:timestamp].to_i flash[:error] = 'This form is stale!' render 'new' else @post.save! @stale_form_check_timestamp = Time.now.to_i session[:last_created_at] = @stale_form_check_timestamp end end 

And here is the form code:

 - form_for @post do |f| = tag :input, :type => 'hidden', :name => 'timestamp', :value => @stale_form_check_timestamp = f.input :some_field = ....... 
+7
source share

When I had the same problem, I created this little stone that solves it. When the user hits back, he is redirected to the edit_path record, instead of returning to new_path .

https://github.com/yossi-shasho/redirect_on_back

You can do something like:

 def create @user = User.new(params[:user]) if result = @user.save redirect_on_back_to edit_user_path(@user) # If user hits 'back' he'll be redirected to edit_user_path redirect_to @user end end 
+4
source share

Your model checks ensure that things like email addresses are unique, but I think this is more about usability and experience than anything else.

Suppose you are talking about an account creation form. First of all, your form submit button should say something like “Create an account,” not just “Submit.” Then, depending on whether it was successful or not, display the message as either "Account created successfully" or "There were errors creating your account." If the user sees this message, they will find out what happened.

I am sure that you cannot stop someone from clicking the "Back" button and pressing "Enter" again, but you should develop for most use cases. If they fall back, they will see a button that says "Create an account." You should probably have different text on the page that says, "Please register for a new account to get started."

Only my $ 0.02.

+2
source share

A session or cookie can cause side effects.

I completely agree: if there is a way to test your model, this is the safest way to prevent duplicate entries.

However, you can do 2 things. Prevent browser caching: fields will be blank on the form when the user clicks the back button . And disable the "Create" button when pressed.

 = f.submit "Create", :disable_with => "Processing..." 

When your user clicks the back button, the button will be disabled.

+2
source share

You can use validators to not insert duplicate values. In this case validates_uniqueness_of :field

If, for example, you want to prevent users from having the same email address, you can put the following code in your user model.

 validates_uniqueness_of :email 

This checks the column for any previous entries that match the ones you are trying to inert. Good luck.

0
source share

base on @Georg Ledermann answer I take this small snapshot of the code to redirect to path editing if the user hits back and then creates hits.

 #objects_controller.rb def new @object = Object.new @stale_form_check = Time.now.to_i end def create @object = Object.new(object_params) #function defined in application_controller.rb redirect_to_on_back_and_create(@object) end 

 #application_controller.rb private def redirect_to_on_back_and_create(object) if session[:last_stale].present? and session[:last_stale_id].present? and session[:last_stale].to_i == params[:stale_form_check].to_i redirect_to edit_polymorphic_path(object.class.find(session[:last_stale_id].to_i)), alert: "Este #{object.model_name.human} ya ha sido creado, puedes editarlo a continuación" else if object.save session[:last_stale] = params[:stale_form_check].to_i session[:last_stale_id] = object.id redirect_to object, notice: "#{object.model_name.human} Creado con éxito" else render :new end end end 

And finally add the @stale_form_check parameter to your form

 <%= hidden_field_tag :stale_form_check, @stale_form_check %> 

You can always abstract this method where you need it, but in this way you could avoid a lot of repetition in your project if you need this behavior in many parts

I hope this helps the next one, I used redirect_on_back gem to use it, but this time it didn’t work for me, the _usec parameter that uses this gemstone was always reset, so it can not be compared every time it was necessary

0
source share

Here is something that worked for me.

You will need to do 2 things: Create a method in your controller and add a conditional statement in the same controller under your create method.

1) Your method should return the total score of this object from this user.

EX:

def user current_user.object.count end

2) Add a conditional statement to your create method.

Example:

def create @object = Object.create (object_params) @ object.save if user == 0 redirect_to x_path end

Hope this helps!

0
source share

Add html: { autocomplete: "off" } to form_for as follows:

 <%= form_for @object, url: xxx_path, html: { autocomplete: "off" } do |f| %> 
0
source share

Source: https://habr.com/ru/post/650926/


All Articles