2008-12-11 11 views
7

Tôi muốn thực hiện một FormWizard 2 phần đơn giản. Mẫu 1 sẽ bằng cách tự động tạo ra một cái gì đó như thế này:Django FormWizard với các hình thức động

class BuyAppleForm(forms.Form): 
    creditcard = forms.ChoiceField(widget = forms.RadioSelect) 
    type = forms.ChoiceField(widget = forms.RadioSelect) 
    def __init__(self,*args, **kwargs): 
     user = kwargs['user'] 
     del kwargs['user'] 

     super(BuyAppleForm, self).__init__(*args, **kwargs) 

     credit_cards = get_credit_cards(user) 
     self.fields['creditcard'].choices = [(card.id,str(card)) for card in credit_cards] 

     apple_types= get_types_packages() 
     self.fields['type'].choices = [(type.id,str(type)) for type in apple_types] 

này tự động sẽ tạo ra một hình thức với danh sách các lựa chọn có sẵn.

Biểu mẫu thứ hai của tôi, tôi thực sự không muốn nhập liệu. Tôi chỉ muốn hiển thị màn hình xác nhận có chứa thông tin thẻ tín dụng, thông tin về táo và số tiền (tổng cộng, thuế, giao hàng). Khi người dùng nhấp OK, tôi muốn mua táo để bắt đầu.

Tôi đã có thể triển khai cách tạo biểu mẫu đơn bằng cách chuyển trong đối tượng request.user trong kwargs. Tuy nhiên, với FormWizard, tôi không thể hình dung ra điều này.

Tôi có đang tiếp cận vấn đề sai và FormWizard không phải là cách thích hợp để thực hiện việc này không? Nếu có, phương thức Form __init__ có thể truy cập đối tượng người dùng từ yêu cầu HTTP như thế nào?

Trả lời

0

Tôi không biết nếu trả lời câu hỏi của riêng mình là hành vi có thể chấp nhận được trên StackOverflow, đây là giải pháp của tôi cho vấn đề của riêng tôi.

Đầu tiên, mương FormWizard.

Tôi có một biểu mẫu. Hai chế độ xem: buy_applesbuy_apples_confirm

Chỉ xử lý lần xem đầu tiên GET. Nó in ra hình thức không liên kết, với một hành động để đi đến URL của lần xem thứ hai.

Chế độ xem thứ hai kiểm tra sự hiện diện của thông số POST có tên "xác nhận". Nếu nó không phải là hiện tại (vì nó là không phải khi xem được nạp lần đầu tiên) nó:

  1. Điều chỉnh widget trên tất cả các lĩnh vực được HiddenInput
  2. Viết ra mẫu mà đưa ra một bản tóm tắt theo thứ tự. Mẫu này cũng đặt trường ẩn có tên "xác nhận" thành 1 (mặc dù trường này không tồn tại trên Biểu mẫu)

Khi người dùng nhấp vào mua táo, biểu mẫu được gửi lại và chế độ xem buy_apples_confirm là gọi thêm một lần nữa. Lần này, thông số POST có tên "xác nhận" hiện diện, vì vậy chúng tôi thực sự xử lý giao dịch mua và người dùng nhận được quả táo của mình.

Tôi hoan nghênh mọi phê bình về phương pháp này hoặc cách xử lý tình huống tốt hơn. Tôi mới đến Django và thấy rằng có rất nhiều cách khác nhau để tiếp cận một vấn đề. Tôi muốn học từ những điều tốt nhất.

4

Tôi chưa sử dụng, nhưng đối với trường hợp bạn mô tả, có vẻ như bạn có thể muốn thử số FormPreview thay vì FormWizard. Từ tài liệu, nó giống như những gì bạn đang theo dõi.

+0

Thú vị. Cảm ơn vì đã chỉ ra điều đó, Tom. – ayaz

+0

Cảm ơn bạn đã chỉ ra FormPreview. Tuy nhiên, trong trường hợp của tôi, một phần của vấn đề đang chuyển vào một giá trị bổ sung ** kwargs (request.user là 'người dùng') cho __init__ của hàm tạo (cần thiết cho tạo biểu mẫu động), và tôi không thấy cách đó có thể với FormPreview. –

+0

Tôi không chắc liệu nó có giúp ích gì không (với FormPreview), nhưng một số thứ bạn có thể làm trong mã của bạn ở trên không được đặt mã đó vào phương thức __init__. ví dụ. x = BuyAppleForm(), sau đó làm x.set_choices_for_user (request.user) trước khi nó được hiển thị. – Tom

0

Cảm ơn bạn krys đã trả lời câu hỏi của riêng bạn. Giúp tôi, nhưng tôi vẫn có một số nhận xét.

FormPreview không phải là cách để đi vì nó theo như tôi biết không hỗ trợ các hình thức động. Nó dựa trên một lớp biểu mẫu cố định để tạo ra từ đó từ đó. Nhưng chúng ta đang tạo động ở đây với một hàm. Có lẽ FormPreview sẽ hỗ trợ này một ngày (hoặc đã làm và tôi không biết làm thế nào).

Giải pháp Krys có vẻ giống như FormPreview. Chỉ băm được bỏ ra, vì vậy người dùng có thể thay đổi dữ liệu trong các trường ẩn hoặc bạn có kiểm tra lại không ?. Nếu bạn kiểm tra lại lần nữa, điều đó sẽ không tuân theo DRY vì bạn sao chép kiểm tra (được, có thể là một phương pháp có thể tái sử dụng, vì vậy chỉ lặp lại rất ít).

Điều tôi đang tự hỏi, bạn điều chỉnh tiện ích như thế nào? Bạn có sao chép biểu mẫu bằng các tiện ích mới hoặc có cách nào để thay đổi động đó không?

5

Khi tôi đang cố gắng tìm ra FormWizard, tôi đã tìm kiếm khắp nơi và thấy phản ứng như hầu hết các mà chỉ nói không sử dụng nó. FormPreview sẽ hoạt động tốt vì OP chỉ quan tâm đến một biểu mẫu một cấp, nhưng câu hỏi vẫn còn hợp lệ trong cách sử dụng FormWizard.

Mặc dù câu hỏi này quá cũ, tôi nghĩ rằng câu trả lời ở đây là rất đáng giá vì tôi đã hỏi rất nhiều trang và tôi không thấy câu trả lời gắn liền với nó, cũng như giải pháp rõ ràng trong tài liệu.

Tôi nghĩ về câu hỏi OP, ghi đè lên process_step là cách để thực hiện. Bí quyết là tạo biểu mẫu (hoặc xem) trong phương thức này để nhận dữ liệu từ biểu mẫu đầu tiên.

tôi thêm form_setup này để forms.py tôi như là một wrapper tiện ích (nghĩ constructor):

def form_setup(**kwargs): 
    def makeform(data, prefix=None, initial=None): 
     form = FormLev2(data, prefix, initial) 
     for k, v in kwargs.items(): 
      if k == 'some_list': 
       form.fields['some_list'].choices = v 
      ... 
     return form 
    return makeform 

Sau đó ghi đè process_step như sau:

def process_step(self, request, process, step): 
    if step == 1 
     if form.is_valid(): #form from step 1 
      objs = Table.objects.filter(...) #based on last form 
      self.form_list[1] = form_setup(some_list=[(o.id,o.name) for o in objs]) #(*) 
    ... 

Bằng cách đó, bạn có thể tự động sửa đổi form_list (*), theo nghĩa là bạn sửa đổi form_list trong cá thể FormWizard, chứ không phải là các định nghĩa biểu mẫu. Hàm wrapper là cần thiết cho chức năng này, vì nó trả về một hàm sẽ khởi tạo một đối tượng Form mới, sau đó được sử dụng trong FormWizard để được gọi với dữ liệu cho biểu mẫu tiếp theo, và cho phép bạn sử dụng dữ liệu từ trước .

Chỉnh sửa: đối với nhận xét của Erik và để làm rõ phần cuối cùng.

Cũng lưu ý rằng process_step sẽ được gọi với bước [0, n] sau bước n.

+0

Dòng 'for k, v in kwargs' cần phải là' cho k, v trong kwargs.items() '. Khác hơn thế, giải pháp tuyệt vời :) – Erik