2011-01-11 12 views
8

「保存」ボタンを使用してデータベースにレコードを追加するフォームを含む「新しい」アクションを備えた単純なRailsスキャフォールドアプリケーションについて考えてみましょう。 「作成」アクションの後、コントローラは「表示」アクションにリダイレクトします。ユーザーは「挿入」されたレコードを編集するために「編集」リンクを使用できます。これまでのところ、とても単純です。Rails:戻るボタンによる重複挿入の防止と再保存

しかし、ユーザーがレコードを作成した後にブラウザの戻るボタンを使用して「新しい」アクションに戻ると、ブラウザはユーザーが入力した値のフォームを表示します。今度はいくつかの値を変更し、もう一度「保存」を押します。彼はこれがレコードを変更すると考えていますが、もちろんこれによって新しいレコードが作成されます。

このような重複したエントリを防ぐには、どのような方法が適していますか?私は一般的な解決策、おそらくクッキーやJavaScriptに基づいて探しています。

+0

提出されるデータは何ですか?私たちが掴むことができるユニークな識別子がありますか? –

+0

私は一般的な解決策について考えているので、どんな種類のデータでも動作するはずです。そして、ユニークでなければならない価値がないならば、それはまた働くべきです。 –

答えて

7

いくつかの調査の後、私はクッキーに基づいて適切な解決策を見つけました。

コントローラの「新しい」アクションでは、現在の時刻のタイムスタンプが生成され、隠しフィールドとしてフォームに表示されます。ユーザーがフォームを送信すると、このタイムスタンプはコントローラーの「作成」アクションに戻ります。レコードを作成した後、このタイムスタンプはセッションCookieに格納されます。ユーザーがブラウザの「戻る」ボタンを使用して「新しい」フォームに戻ると、彼は失効したフォームを取得します。これは、そのタイムスタンプがCookieに保存されているものより古いことを意味します。これは、レコードを作成する前にチェックされ、エラーメッセージが表示されます。

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 

そしてここでフォームコード:ここで

は、コントローラのコードである

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

このソリューションは、私が解決策を実装しようとしたセッションが消去されるまで、後で新しい投稿を作成することはできませんが、一度しか動作しません(エラー後にlast_created_atを解放し、|| = Time .now.to_i)。 悲しいことに、このソリューションではもう一度批評に失敗しても、変数は決して解放されず、以前のエラーと同じです:/ – Alexis

0

バリデータを使用して重複する値が挿入されていないことを確認できます。この場合validates_uniqueness_of :field

たとえば、ユーザーが同じ電子メールアドレスを使用しないようにするには、ユーザーモデルに次のコードを入力します。

validates_uniqueness_of :email 

これは、不活性化しようとしているものと同じ以前のエントリがないか列をチェックします。 幸運

+0

あなたは正しいですが、これは一意でなければならないフィールドがある場合にのみ機能します。私はより一般的な解決策を探しています。 –

2

あなたのモデルの検証は、電子メールアドレスのようなものが一意であることを保証しますが、これは他のものよりも使いやすさと経験についてです。

あなたがアカウント作成フォームについて話しているとします。まず、フォーム提出ボタンには、「送信」の代わりに「アカウントの作成」のようなものが必要です。その後、成功したかどうかによって、「アカウントが正常に作成されました」、または「アカウントの作成中にエラーが発生しました」のようなメッセージが表示されます。ユーザーがこのメッセージを見ると、何が起こったのかが分かります。

確かに、誰かが戻るボタンを押して再度入力するのを防ぐことはできませんが、大部分のユースケースを設計する必要があります。ヒットした場合、「アカウントの作成」ボタンが表示されます。あなたはおそらく "スタートするために新しいアカウントにサインアップしてください"というページに他のテキストがあるはずです。

ちょうど$ 0.02です。

+0

絶対に:http://uxmovement.com/forms/why-your-form-b​​uttons-should-never-say-submit –

+1

はい、洗練されたボタンラベルは、ユーザーが「アカウントの作成」をもう一度クリックしないようにすることができます。私はそれが単純な "提出"であってはならないとあなたに同意します。しかし、IMHOはこれをしません。ボタンテキストを無視し、既存のレコードを変更できると思うユーザーは常にいます。 私はそのようなことを認識し、「同じレコードを2回追加しようとしています!」というメッセージを表示する方法を探しています。代わりに、またはさらに、作成の代わりに更新を行うこともできます。 –

+1

ユーザーが実際に同じレコードを再度入力したい場合はどうなりますか? –

2

セッションやクッキーは、側面の効果をもたらすことができます。

私は全く同意します:あなたのモデルで検証する方法があれば、重複するレコードを防ぐ最も安全な方法です。

まだ2つのことができます。ブラウザのキャッシングを防ぐ:ユーザーがclicks on the back buttonのときにフォームに空欄が表示されます。クリックすると「作成」ボタンが無効になります。

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

ユーザーが戻るボタンを押すと、このボタンは無効になります。

3

私が同じ問題を抱えたとき、私はそれを解決するこの小さな宝石を作りました。ユーザーがヒットすると、new_pathに戻るのではなく、レコードのedit_pathにリダイレクトされます。

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

あなたは何かのように行うことができます:私は、ユーザーが戻ってヒットしてから作成ヒットした場合、リダイレクト用のコードのこの小さなスニップは、パスを編集するために作る@Georg Ledermannの答えに

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 
+0

これは素晴らしく、activeadminで使用しようとしましたが、 redirect_on_back_toを取得してください:(次回はAAdminなしでこの問題が発生したので、別のショットを付けます。 – Alexis

+0

@Alexisご連絡いただきありがとうございます。問題を説明したり、問題を開くことはできますかhttps://github.com/yossi -shasho/redirect_on_back/issues? –

+0

私はそれが問題であったかどうかは分かりませんが、私の場合、activeadminが動作を作成して機能を実装しようとしましたが、ちょっとややこしいと思っています。私はそれらに完全に精通していない、別のアイデアは、その時に来た可能性が多分そこに機能を実装していなかったが、私の知識は、私はこのアイデアをもっと試してみましょう、後で通常のコントローラと魅力のように動作します、とにかく! – Alexis

0

ベースを。

#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 


そして最後には、フォームに@stale_form_checkのPARAMを追加

<%= hidden_field_tag :stale_form_check, @stale_form_check %> 

あなたは常にあなたがそれを必要とする、このメソッドを抽象化できますが、この方法であなたは可能性が多くの部分でこの振る舞いが必要な場合は、プロジェクトの繰り返しを避けてください。

私はredirect_on_back gemを使用していましたが、次回の使用には役立ちますが、今回は私のためには機能しませんでした。この宝石が使用する_usecパラメータは常にリセットされていました。それは必要でした

0

私のために働いたものはここにあります。

あなたは2つのことをする必要があります:あなたのコントローラーにメソッドを作成し、そのコントローラーに「作成」メソッドの条件ステートメントを追加します。

1)メソッドは、そのオブジェクトからそのオブジェクトの総数を返す必要があります。

EX:

デフユーザー current_user.object.count エンド

2)あなたの '作成' 方法で条件文を追加します。

例:

DEF、私はこのことができます願っていたユーザ== 0 redirect_to x_path エンド

場合 @object = Object.create(object_params)object.save @ を作成します!

関連する問題