2010-01-26 38 views
17

Tôi đang cố gắng tìm cách ngăn người dùng gửi hai lần biểu mẫu của tôi. Tôi có javascript vô hiệu hóa nút gửi, nhưng vẫn có một người dùng không thường xuyên tìm cách gửi hai lần.Có một thư viện để ngăn chặn đệ trình mẫu trùng lặp tồn tại cho django?

Tôi có tầm nhìn về thư viện có thể sử dụng lại mà tôi có thể tạo để bảo vệ khỏi điều này.

Trong thư viện lý tưởng của tôi, khối mã sẽ giống như thế này:

try: 
    with acquire_lock({'field1':'abc', 'field2':'def'}) as lock: 
     response = #do some credit card processing 
     lock.response = response 
except SubmissionWasDuplicate, e: 
    response = e.response 

Bảng khóa sẽ giống như thế này:

duplicate_submission_locks

  • submission_hash # một MD5 của đối số đã gửi
  • phản hồi # dữ liệu đã chọn
  • created_at # sử dụng để quét bảng này
  • lock_expired # boolean nghĩa nếu khóa đã hết hạn

Có ai biết nếu điều này đã tồn tại? Nó có vẻ không khó để viết, vì vậy nếu nó không tồn tại tôi có thể viết nó bản thân mình.

+0

Hãy nhìn vào câu hỏi này http: // stackoverflow. com/questions/320096/django-how-can-i-bảo vệ-chống-đồng thời-sửa đổi-of-dữ liệu-cơ sở-mục nó có một số ý tưởng tốt về khóa lạc quan –

+0

Khóa lạc quan là gần với những gì tôi cần, ngoại trừ 2 nhiều thứ. 1) Đây là biểu mẫu đăng ký, do đó, không có trường mô hình để cập nhật. 2) Nếu đây là lần gửi đôi, tôi muốn cả hai lần gửi để hiển thị trang thành công hoặc trang thử lại sẽ phù hợp. – Gattster

Trả lời

6

Một giải pháp dễ dàng cho vấn đề này là thêm một băm duy nhất cho mỗi biểu mẫu. Sau đó, bạn có thể có một bảng cuộn các biểu mẫu hiện tại. Khi một biểu mẫu được gửi, hoặc băm quá cũ, bạn có thể hết hạn nó ra khỏi bảng của bạn, và từ chối bất kỳ biểu mẫu nào không có hàm băm phù hợp trong bảng của bạn.

HTTPRedirect là cách chính xác để thực hiện, như đã đề cập trước đây.

Thật không may, ngay cả khi riêng của Django được xây dựng trong quản trị là dễ bị các vấn đề liên quan đến vấn đề này. Trong một số trường hợp, khuôn khổ kịch bản cross-site có thể hỗ trợ để ngăn chặn một số điều này, nhưng tôi e rằng các phiên bản sản xuất hiện tại không có tích hợp sẵn.

+0

Tôi tự hỏi liệu điều này có bị ảnh hưởng không nếu người dùng sử dụng nút quay lại của mình và gửi lại cùng một biểu mẫu với các trường khác nhau? Chúng tôi sẽ cần phải làm mới băm duy nhất. – Gattster

+0

Yêu cầu của bạn là để ngăn chặn gửi lại. Loại bỏ băm từ DB nên làm điều đó một cách thủ công. Một biểu mẫu được gửi với các giá trị hơi khác nhau vẫn được gửi lại trong hầu hết các trường hợp, mặc dù bạn có thể sử dụng hàm băm để lưu trữ tham chiếu chuyển tiếp tạm thời cho việc gửi, cho phép chỉnh sửa một lần gửi trước đó trong một khoảng thời gian hữu hạn. –

4

Thành thật mà nói, đặt cược tốt nhất của bạn (thực hành dễ dàng và tốt) là đưa ra HTTPRedirect() để các trang cảm ơn, và nếu cảm ơn bạn trang là một trong những giống như hình thức, đó là OK. Bạn vẫn có thể làm điều này.

+0

Cảm ơn bạn đã trả lời. Làm một HTTPRedirect để hiển thị trang cảm ơn là những gì tôi đã có trong tâm trí. Câu hỏi này là nhiều hơn về cách phát hiện rằng bài đăng là trùng lặp. – Gattster

2

Bạn nên sử dụng chuyển hướng sau -post phương pháp. Điều này ngăn người dùng vô tình gửi lại biểu mẫu bằng chức năng làm mới từ trình duyệt. Nó cũng hữu ích ngay cả khi bạn sử dụng phương thức băm. Đó là vì không có chuyển hướng sau POST, trong trường hợp nhấn nút Back/Refresh, người dùng sẽ thấy một thông báo câu hỏi về việc gửi lại biểu mẫu, điều này có thể gây nhầm lẫn cho cô ấy.

Nếu bạn thực hiện chuyển hướng GET sau mỗi POST, sau đó nhấn Quay lại/Làm mới sẽ không hiển thị thông báo này (cho người dùng thông thường). Vì vậy, để bảo vệ đầy đủ sử dụng Hash + chuyển hướng sau khi đăng bài.

10

Bạn có thể sử dụng một phiên để lưu trữ các hash

import hashlib 

def contact(request): 
    if request.method == 'POST': 
     form = MyForm(request.POST) 
     #join all the fields in one string 
     hashstring=hashlib.sha1(fieldsstring) 
     if request.session.get('sesionform')!=hashstring: 
      if form.is_valid() :           
       request.session['sesionform'] = hashstring 
       #do some stuff... 
       return HttpResponseRedirect('/thanks/') # Redirect after POST 
     else 
      raise SubmissionWasDuplicate("duplicate") 
    else: 
     form = MyForm() 

Với phương pháp này (không xóa các cookie phiên) người dùng có thể không tái lưu trữ dữ liệu util phiên sẽ hết hạn, bằng cách này, tôi giả sử tồn tại một cái gì đó xác định người dùng gửi dữ liệu

+0

Điều gì xảy ra nếu người dùng truy cập trang/thanks/và giao dịch chưa kết thúc? Ngoài ra, nếu giao dịch thất bại thì sao? – Gattster

+1

nếu giao dịch không thành công, bạn phải kết thúc phiên và chuyển hướng đến một số trang lỗi. Nếu bạn muốn đảm bảo rằng chỉ có thể truy cập trang "/ thanks" khi giao dịch hoàn tất và hợp lệ, bạn phải thêm mã thông báo khi giao dịch hoàn tất và gửi tới trang "/ thanks" và xác thực ở đó (giống như paypal hiện) –

3

câu trả lời của Kristian Damian thực sự là một gợi ý tuyệt vời. Tôi chỉ nghĩ về một biến thể nhỏ về chủ đề đó, nhưng nó có thể có nhiều chi phí hơn.

Bạn có thể thử triển khai một cái gì đó được sử dụng trong django-piston cho các đối tượng BaseHandler, là phương pháp được gọi là exists() để kiểm tra xem nội dung bạn đang gửi đã có trong cơ sở dữ liệu chưa.

Từ handler.py (BaseHandler):

def exists(self, **kwargs): 
    if not self.has_model(): 
     raise NotImplementedError 

    try: 
     self.model.objects.get(**kwargs) 
     return True 
    except self.model.DoesNotExist: 
     return False 

Vì vậy, chúng ta hãy nói chắc rằng một chức năng gọi là request_exists(), thay vì một phương pháp:

if form.is_valid() 
    if request_exists(request): 
     # gracefully reject dupe submission 
    else: 
     # do stuff to save the request 
     ... 
     # and ALWAYS redirect after a POST!! 
     return HttpResponseRedirect('/thanks/') 
+0

Điều này sẽ hoạt động. Câu hỏi chính mà điều này đặt ra cho tôi là làm thế nào để xử lý một cách duyên dáng việc đệ trình. Vì lần gửi đầu tiên có thể chưa hoàn tất, tôi cần phải trì hoãn cho đến khi lần gửi đầu tiên kết thúc. Sau đó, tôi cần phải gửi người dùng đến trang cảm ơn và tìm thấy kết quả của việc gửi, có thể đã thành công hoặc thất bại với một thông báo lỗi. – Gattster

+0

Bạn không phải gửi chúng đến trang "/ thanks /" trần truồng. Bạn có thể gửi nó cùng với dữ liệu yêu cầu để mẫu có thể xử lý các kết quả của việc gửi và hiển thị chúng cho phù hợp. Có lẽ trang "/ thêm/xác nhận /" xen kẽ sẽ tốt trước khi bạn truy cập "/ thanks /"? – jathanism

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