2011-01-11 41 views
8

Hãy nghĩ về ứng dụng giàn giáo Rails đơn giản với hành động "mới" chứa biểu mẫu để thêm bản ghi vào cơ sở dữ liệu bằng nút "lưu". Sau khi hành động "tạo", bộ điều khiển chuyển hướng đến hành động "hiển thị", trong đó người dùng có thể sử dụng liên kết "chỉnh sửa" để chỉnh sửa bản ghi vừa được chèn. Cho đến nay, rất đơn giản.Đường ray: Ngăn chặn chèn trùng lặp do nhấn nút quay lại và lưu lại

Nhưng nếu người dùng thay vì sử dụng nút quay lại của trình duyệt sau khi tạo bản ghi để quay lại hành động "mới", trình duyệt sẽ hiển thị biểu mẫu với các giá trị mà người dùng vừa nhập. Bây giờ anh ta thay đổi một số giá trị và nhấn "lưu" một lần nữa. Ông nghĩ rằng điều này sẽ thay đổi kỷ lục, nhưng tất nhiên điều này tạo ra một kỷ lục mới.

Cách ưa thích để ngăn các mục trùng lặp như vậy là gì? Tôi đang tìm một giải pháp chung, có thể dựa trên cookie hoặc JavaScript.

+0

Dữ liệu đang được gửi là gì?có bất kỳ số nhận dạng duy nhất nào mà chúng tôi có thể nắm giữ không? –

+0

Tôi đang suy nghĩ về một giải pháp chung, vì vậy nó sẽ làm việc cho bất kỳ loại dữ liệu nào. Và như vậy, nó cũng nên làm việc nếu có giá trị NO mà phải là duy nhất. –

Trả lời

7

Sau một số điều tra, tôi đã tìm thấy giải pháp phù hợp dựa trên cookie. Đây là:

Trong hành động "mới" của bộ điều khiển, dấu thời gian với thời gian hiện tại được tạo và hiển thị dưới dạng trường ẩn. Khi người dùng gửi biểu mẫu, dấu thời gian này trở lại hành động "tạo" của bộ điều khiển. Sau khi tạo bản ghi, dấu thời gian này được lưu trữ trong cookie phiên. Nếu người dùng quay trở lại biểu mẫu "mới" thông qua nút quay lại của trình duyệt, anh ta nhận được một biểu mẫu cũ, có nghĩa là dấu thời gian của nó cũ hơn thẻ được lưu trữ trong cookie. Điều này được kiểm tra trước khi tạo bản ghi và dẫn đến thông báo lỗi.

Đây là mã điều khiển:

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 

Và đây mẫu mã:

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

giải pháp này sẽ không cho phép tạo Bài đăng mới cho đến khi phiên xóa, tôi đã cố gắng triển khai giải pháp tải lại, nhưng chỉ cần quản lý một lần (giải phóng: last_created_at sau lỗi và điền vào mới bằng || = Thời gian .now.to_i). Đáng buồn là giải pháp này có một nhà phê bình thất bại nếu bạn quay trở lại một lần nữa, sau đó biến không bao giờ được phát hành và lỗi giống như trước:/ – Alexis

0

Bạn có thể sử dụng trình xác thực để đảm bảo không chèn giá trị trùng lặp nào. Trong trường hợp này, validates_uniqueness_of :field

Ví dụ: nếu bạn muốn ngăn người dùng có cùng địa chỉ email, bạn có thể đặt mã sau vào mẫu người dùng của mình.

validates_uniqueness_of :email 

Cột này kiểm tra cột cho bất kỳ mục nhập nào trước đó giống với mục bạn cố gắng trơ. Chúc may mắn

+0

Bạn nói đúng, nhưng điều này chỉ hoạt động nếu có một trường phải là duy nhất. Tôi đang tìm một giải pháp tổng quát hơn. –

2

Xác thực mô hình của bạn sẽ đảm bảo những thứ như địa chỉ email là duy nhất, nhưng tôi nghĩ điều này là nhiều hơn về khả năng sử dụng và trải nghiệm hơn bất kỳ thứ gì khác.

Giả sử bạn đang nói về biểu mẫu tạo tài khoản. Trước hết, nút gửi biểu mẫu của bạn nên nói điều gì đó như "Tạo tài khoản", thay vì chỉ "Gửi". Sau đó, tùy thuộc vào việc nó có thành công hay không, hiển thị thông báo như "Tài khoản được tạo thành công" hoặc "Đã xảy ra lỗi khi tạo tài khoản của bạn". Nếu người dùng thấy thông báo này, họ sẽ biết chuyện gì đã xảy ra.

Chắc chắn bạn không thể ngăn ai đó nhấn nút quay lại và nhấn lại, nhưng bạn nên thiết kế cho phần lớn các trường hợp sử dụng. Nếu chúng xảy ra để quay lại, họ sẽ thấy nút có nội dung "Tạo tài khoản". Bạn có thể có một số văn bản khác trên trang có nội dung "Vui lòng đăng ký tài khoản mới để bắt đầu".

Chỉ $ 0,02 của tôi.

+0

Tuyệt đối: http://uxmovement.com/forms/why-your-form-buttons-should-never-say-submit –

+1

Có, một nhãn nút tinh vi có thể ngăn người dùng nhấp vào "tạo tài khoản" lần thứ hai. Tôi đồng ý với bạn rằng nó không phải là một đơn giản "Gửi". Nhưng IMHO điều này sẽ không làm điều đó. Luôn có người dùng bỏ qua văn bản nút và nghĩ rằng họ có thể thay đổi bản ghi hiện có. Tôi đang tìm cách nhận ra những thứ như vậy và hiển thị thông báo như "Bạn đang cố gắng thêm cùng một bản ghi hai lần!" thay vào đó, hoặc thậm chí tốt hơn, thực hiện cập nhật thay vì tạo. –

+1

Điều gì xảy ra nếu người dùng thực sự muốn nhập lại cùng một bản ghi? –

2

phiên hoặc cookie có thể dẫn đến hiệu ứng bên.

Tôi hoàn toàn đồng ý: nếu có cách để xác thực với mô hình của bạn, đó là cách an toàn nhất để ngăn các bản ghi trùng lặp.

Bạn vẫn có thể làm 2 việc. Ngăn chặn bộ nhớ đệm của trình duyệt: các trường sẽ xuất hiện trống trong biểu mẫu khi người dùng clicks on the back button. Và vô hiệu hóa nút "Tạo" khi được nhấp.

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

Khi người dùng của bạn sẽ nhấn nút quay lại, nút sẽ bị tắt.

3

Khi tôi gặp vấn đề tương tự, tôi đã tạo ra viên đá quý nhỏ này để giải quyết nó. Khi người dùng truy cập lại, anh được chuyển hướng đến số edit_path của bản ghi, thay vì quay lại số new_path.

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

Bạn có thể làm một cái gì đó như:

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

trông đẹp, tôi đã cố gắng sử dụng nó với activeadmin, nhưng tôi không thể nhận được redirect_on_back_to làm việc :(, lần sau tôi có vấn đề này mà không có AAdmin, tôi sẽ cung cấp cho nó một shot, cảm ơn! – Alexis

+0

@Alexis xin lỗi để nghe, bạn có thể mô tả vấn đề hoặc mở một vấn đề ở đây https://github.com/yossi –

+0

Tôi không chắc chắn nếu nó là một vấn đề, trong trường hợp của tôi tôi đã cố gắng để ghi đè lên activeadmin tạo hành động và thực hiện các chức năng, tôi đoán chỉ là một chút khéo léo, activeadmin sử dụng tài nguyên vốn có và tôi không hoàn toàn quen thuộc với những thứ đó, một ý tưởng khác xuất hiện vào thời điểm đó là có lẽ nó không triển khai các chức năng ở đó, nhưng kiến ​​thức của tôi không cho phép tôi thử thêm ý tưởng này, tôi sẽ sử dụng nó sau bộ điều khiển bình thường và hoạt động như một nét duyên dáng, liên quan! – Alexis

0

dựa trên @Georg Ledermann câu trả lời tôi làm snip này ít mã cho chuyển hướng để chỉnh sửa đường dẫn nếu người dùng chạm trở lại và sau đó chạm tạo.

#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 

Và cuối cùng thêm param @stale_form_check mẫu của bạn

<%= hidden_field_tag :stale_form_check, @stale_form_check %> 

Bạn luôn có thể tóm tắt phương pháp này, nơi bạn cần đến nó, nhưng theo cách này bạn có thể tránh nhiều sự lặp lại trong dự án của bạn nếu bạn cần hành vi này ở nhiều phần

Hy vọng nó sẽ giúp cái tiếp theo, tôi đã từng sử dụng gem gem redirect_on_back, nhưng nó không làm việc cho tôi lần này, tham số _usec mà gem này sử dụng, luôn được đặt lại, vì vậy nó không thể so sánh trong mọi lúc nó đã cần

0

Đây là một cái gì đó đã làm việc cho tôi.

Bạn sẽ cần phải làm 2 việc: Tạo một phương thức trong bộ điều khiển của bạn và thêm câu lệnh có điều kiện vào cùng một bộ điều khiển trong phương thức 'tạo' của bạn.

1) Phương thức của bạn phải trả về tổng số đối tượng đó từ người dùng đó.

EX:

dùng def current_user.object.count cuối

2) Thêm câu lệnh điều kiện trong phương pháp 'tạo ra' của bạn.

VÍ DỤ:

def tạo @object = Object.create (object_params) @ object.save nếu người dùng == 0 redirect_to x_path cuối

Tôi hy vọng điều này sẽ giúp!

Các vấn đề liên quan