Cách thích hợp để xử lý nhiều biểu mẫu trên một trang trong Django


202

Tôi có một trang mẫu mong đợi hai hình thức. Nếu tôi chỉ sử dụng một hình thức, mọi thứ sẽ ổn như trong ví dụ điển hình này:

if request.method == 'POST':
    form = AuthorForm(request.POST,)
    if form.is_valid():
        form.save()
        # do something.
else:
    form = AuthorForm()

Tuy nhiên, nếu tôi muốn làm việc với nhiều biểu mẫu, làm cách nào để cho chế độ xem biết rằng tôi chỉ gửi một trong các biểu mẫu chứ không phải biểu mẫu khác (nghĩa là vẫn yêu cầu. Tôi chỉ muốn xử lý biểu mẫu mà việc gửi đã xảy ra)?


Đây là giải pháp dựa trên câu trả lời trong đó cụm từ dự kiếncụm từ bị cấm là tên của các nút gửi cho các hình thức khác nhau và dự kiếnphraseformcấmphraseform là các hình thức.

if request.method == 'POST':
    if 'bannedphrase' in request.POST:
        bannedphraseform = BannedPhraseForm(request.POST, prefix='banned')
        if bannedphraseform.is_valid():
            bannedphraseform.save()
        expectedphraseform = ExpectedPhraseForm(prefix='expected')
    elif 'expectedphrase' in request.POST:
        expectedphraseform = ExpectedPhraseForm(request.POST, prefix='expected')
        if expectedphraseform.is_valid():
            expectedphraseform.save() 
        bannedphraseform = BannedPhraseForm(prefix='banned')
else:
    bannedphraseform = BannedPhraseForm(prefix='banned')
    expectedphraseform = ExpectedPhraseForm(prefix='expected')

2
Không có lỗi logic với giải pháp của bạn? Nếu bạn đăng 'cụm từ bị cấm', dự kiến ​​sẽ không được điền.
Ztyx

2
Điều này sẽ chỉ xử lý một biểu mẫu cùng một lúc, câu hỏi là về việc xử lý nhiều biểu mẫu cùng một lúc
chiếu

Câu trả lời:


141

Bạn có một vài lựa chọn:

  1. Đặt các URL khác nhau trong hành động cho hai hình thức. Sau đó, bạn sẽ có hai chức năng xem khác nhau để đối phó với hai hình thức khác nhau.

  2. Đọc các giá trị nút gửi từ dữ liệu POST. Bạn có thể cho biết nút gửi nào đã được nhấp: Làm cách nào tôi có thể tạo nhiều nút gửi biểu mẫu django?


5
3) Xác định hình thức nào được gửi từ tên trường trong dữ liệu POST. Bao gồm một số đầu vào ẩn nếu từ của bạn không có các trường duy nhất với tất cả các giá trị có thể không trống.
Denis Otkidach

13
4) Thêm một trường ẩn xác định biểu mẫu và kiểm tra giá trị của trường này trong chế độ xem của bạn.
Soviut

Tôi sẽ tránh xa việc gây ô nhiễm dữ liệu POST nếu có thể. Tôi khuyên bạn nên thêm một tham số GET vào url hành động biểu mẫu thay thế.
pygeek

6
# 1 thực sự là đặt cược tốt nhất của bạn ở đây. Bạn không muốn làm ô nhiễm POST của mình với các trường ẩn và bạn cũng không muốn buộc chế độ xem của mình vào mẫu và / hoặc biểu mẫu của bạn.
thiên thạch

5
@meteorainer nếu bạn sử dụng số một, có cách nào để chuyển các lỗi trở lại các biểu mẫu trong chế độ xem cha mẹ để khởi tạo chúng, mà không sử dụng khung thông báo hoặc chuỗi truy vấn không? Câu trả lời này có vẻ gần nhất, nhưng ở đây vẫn chỉ là một chế độ xem xử lý cả hai hình thức: stackoverflow.com/a/21271659/2532070
YPCrumble

45

Một phương pháp để tham khảo trong tương lai là một cái gì đó như thế này. Cấmphraseform là hình thức đầu tiên và dự kiến ​​là hình thức thứ hai. Nếu cái đầu tiên bị đánh, cái thứ hai bị bỏ qua (đó là một giả định hợp lý trong trường hợp này):

if request.method == 'POST':
    bannedphraseform = BannedPhraseForm(request.POST, prefix='banned')
    if bannedphraseform.is_valid():
        bannedphraseform.save()
else:
    bannedphraseform = BannedPhraseForm(prefix='banned')

if request.method == 'POST' and not bannedphraseform.is_valid():
    expectedphraseform = ExpectedPhraseForm(request.POST, prefix='expected')
    bannedphraseform = BannedPhraseForm(prefix='banned')
    if expectedphraseform.is_valid():
        expectedphraseform.save()

else:
    expectedphraseform = ExpectedPhraseForm(prefix='expected')

7
sử dụng tiền tố = thực sự là 'cách thích hợp'
Rich

tiền tố-kwarg đã làm công việc, tốt đẹp!
Stephan Hoyer

1
Ý tưởng tuyệt vời với những tiền tố đó, chúng tôi đã sử dụng chúng ngay bây giờ và chúng hoạt động như một bùa mê. Nhưng chúng tôi vẫn phải chèn một trường ẩn để phát hiện biểu mẫu nào đã được gửi, bởi vì cả hai biểu mẫu đều nằm trong hộp đèn (mỗi biểu mẫu riêng biệt). Bởi vì chúng tôi cần mở lại hộp đèn chính xác, chúng tôi cần biết chính xác biểu mẫu nào đã được gửi và sau đó nếu biểu mẫu đầu tiên có bất kỳ lỗi xác thực nào, biểu mẫu thứ hai sẽ tự động thắng và biểu mẫu đầu tiên được đặt lại, mặc dù chúng tôi vẫn cần hiển thị các lỗi từ hình thức đầu tiên. Chỉ cần nghĩ rằng bạn nên biết
Enduriel

Sẽ không phải là vụng về để mở rộng mô hình này cho trường hợp của ba hình thức? Giống như, với việc kiểm tra is_valid () từ biểu mẫu đầu tiên, sau đó là hai lần đầu tiên, v.v ... Có lẽ chỉ cần có một handled = Falsebản cập nhật Truekhi tìm thấy một biểu mẫu tương thích?
binki

14

Các khung nhìn dựa trên lớp của Django cung cấp một FormView chung nhưng cho tất cả các ý định và mục đích, nó được thiết kế để chỉ xử lý một biểu mẫu.

Một cách để xử lý nhiều biểu mẫu với cùng một url hành động đích bằng cách sử dụng các chế độ xem chung của Django là mở rộng 'TemplateView' như hiển thị bên dưới; Tôi sử dụng cách tiếp cận này thường xuyên đủ để tôi biến nó thành một mẫu IDE Eclipse.

class NegotiationGroupMultifacetedView(TemplateView):
    ### TemplateResponseMixin
    template_name = 'offers/offer_detail.html'

    ### ContextMixin 
    def get_context_data(self, **kwargs):
        """ Adds extra content to our template """
        context = super(NegotiationGroupDetailView, self).get_context_data(**kwargs)

        ...

        context['negotiation_bid_form'] = NegotiationBidForm(
            prefix='NegotiationBidForm', 
            ...
            # Multiple 'submit' button paths should be handled in form's .save()/clean()
            data = self.request.POST if bool(set(['NegotiationBidForm-submit-counter-bid',
                                              'NegotiationBidForm-submit-approve-bid',
                                              'NegotiationBidForm-submit-decline-further-bids']).intersection(
                                                    self.request.POST)) else None,
            )
        context['offer_attachment_form'] = NegotiationAttachmentForm(
            prefix='NegotiationAttachment', 
            ...
            data = self.request.POST if 'NegotiationAttachment-submit' in self.request.POST else None,
            files = self.request.FILES if 'NegotiationAttachment-submit' in self.request.POST else None
            )
        context['offer_contact_form'] = NegotiationContactForm()
        return context

    ### NegotiationGroupDetailView 
    def post(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)

        if context['negotiation_bid_form'].is_valid():
            instance = context['negotiation_bid_form'].save()
            messages.success(request, 'Your offer bid #{0} has been submitted.'.format(instance.pk))
        elif context['offer_attachment_form'].is_valid():
            instance = context['offer_attachment_form'].save()
            messages.success(request, 'Your offer attachment #{0} has been submitted.'.format(instance.pk))
                # advise of any errors

        else 
            messages.error('Error(s) encountered during form processing, please review below and re-submit')

        return self.render_to_response(context)

Mẫu html có tác dụng như sau:

...

<form id='offer_negotiation_form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8">
    {% csrf_token %}
    {{ negotiation_bid_form.as_p }}
    ...
    <input type="submit" name="{{ negotiation_bid_form.prefix }}-submit-counter-bid" 
    title="Submit a counter bid"
    value="Counter Bid" />
</form>

...

<form id='offer-attachment-form' class="content-form" action='./' enctype="multipart/form-data" method="post" accept-charset="utf-8">
    {% csrf_token %}
    {{ offer_attachment_form.as_p }}

    <input name="{{ offer_attachment_form.prefix }}-submit" type="submit" value="Submit" />
</form>

...

1
Tôi đang vật lộn với vấn đề tương tự này và đã cố gắng tìm cách xử lý mỗi bài đăng trong một dạng xem riêng biệt và sau đó chuyển hướng đến một dạng xem mẫu chung. Vấn đề là làm cho chế độ xem mẫu chịu trách nhiệm về nội dung nhận và chế độ xem biểu mẫu để lưu. xác nhận là một vấn đề mặc dù. lưu các biểu mẫu vào phiên trong tâm trí tôi ... Vẫn đang tìm kiếm một giải pháp sạch.
Daniele Bernardini

14

Tôi cần nhiều hình thức được xác nhận độc lập trên cùng một trang. Các khái niệm chính tôi bị thiếu là 1) sử dụng tiền tố biểu mẫu cho tên nút gửi và 2) một biểu mẫu không giới hạn không kích hoạt xác thực. Nếu nó giúp được ai khác, đây là ví dụ đơn giản hóa của tôi về hai hình thức AForm và BForm bằng TemplateView dựa trên các câu trả lời của @ adam-nelson và @ daniel-sokolowski và nhận xét của @zeraien ( https://stackoverflow.com/a/17303480 / 2680449 ):

# views.py
def _get_form(request, formcls, prefix):
    data = request.POST if prefix in request.POST else None
    return formcls(data, prefix=prefix)

class MyView(TemplateView):
    template_name = 'mytemplate.html'

    def get(self, request, *args, **kwargs):
        return self.render_to_response({'aform': AForm(prefix='aform_pre'), 'bform': BForm(prefix='bform_pre')})

    def post(self, request, *args, **kwargs):
        aform = _get_form(request, AForm, 'aform_pre')
        bform = _get_form(request, BForm, 'bform_pre')
        if aform.is_bound and aform.is_valid():
            # Process aform and render response
        elif bform.is_bound and bform.is_valid():
            # Process bform and render response
        return self.render_to_response({'aform': aform, 'bform': bform})

# mytemplate.html
<form action="" method="post">
    {% csrf_token %}
    {{ aform.as_p }}
    <input type="submit" name="{{aform.prefix}}" value="Submit" />
    {{ bform.as_p }}
    <input type="submit" name="{{bform.prefix}}" value="Submit" />
</form>

Tôi nghĩ rằng đây thực sự là một giải pháp sạch. Cảm ơn.
chhantyal

Tôi thực sự thích giải pháp này. Một câu hỏi: có lý do tại sao _get_form () không phải là một phương thức của lớp MyView không?
cuộc không kích

1
@ AndréTerra chắc chắn là có thể, mặc dù bạn có thể muốn có nó trong một lớp chung kế thừa từ TemplateView để bạn có thể sử dụng lại nó trong các chế độ xem khác.
ybendana

1
Đây là một giải pháp tuyệt vời. Tôi cần thay đổi một dòng của __get_form để nó hoạt động: data = request.POST if prefix in next(iter(request.POST.keys())) else None Nếu không inthì không hoạt động.
larapsodia 27/8/2016

Sử dụng một thẻ <form> như thế này có nghĩa là các trường bắt buộc được yêu cầu trên toàn cầu khi chúng phải là mỗi biểu mẫu tùy thuộc vào nút gửi nào được nhấp. Chia thành hai thẻ <form> (có cùng hành động) hoạt động.
Flash

3

Muốn chia sẻ giải pháp của tôi khi Django Forms không được sử dụng. Tôi có nhiều thành phần biểu mẫu trên một trang và tôi muốn sử dụng một chế độ xem để quản lý tất cả các yêu cầu POST từ tất cả các biểu mẫu.

Những gì tôi đã làm là tôi đã giới thiệu một thẻ đầu vào vô hình để tôi có thể chuyển một tham số cho các khung nhìn để kiểm tra biểu mẫu nào đã được gửi.

<form method="post" id="formOne">
    {% csrf_token %}
   <input type="hidden" name="form_type" value="formOne">

    .....
</form>

.....

<form method="post" id="formTwo">
    {% csrf_token %}
    <input type="hidden" name="form_type" value="formTwo">
   ....
</form>

lượt xem

def handlemultipleforms(request, template="handle/multiple_forms.html"):
    """
    Handle Multiple <form></form> elements
    """
    if request.method == 'POST':
        if request.POST.get("form_type") == 'formOne':
            #Handle Elements from first Form
        elif request.POST.get("form_type") == 'formTwo':
            #Handle Elements from second Form

Tôi nghĩ rằng đây là một cách tốt và dễ dàng hơn
Shedrack

2

Đây là một chút muộn, nhưng đây là giải pháp tốt nhất tôi tìm thấy. Bạn tạo một từ điển tra cứu cho tên biểu mẫu và lớp của nó, bạn cũng phải thêm một thuộc tính để xác định biểu mẫu và trong chế độ xem của bạn, bạn phải thêm nó dưới dạng một trường ẩn, với form.formlabel.

# form holder
form_holder = {
    'majeur': {
        'class': FormClass1,
    },
    'majsoft': {
        'class': FormClass2,
    },
    'tiers1': {
        'class': FormClass3,
    },
    'tiers2': {
        'class': FormClass4,
    },
    'tiers3': {
        'class': FormClass5,
    },
    'tiers4': {
        'class': FormClass6,
    },
}

for key in form_holder.keys():
    # If the key is the same as the formlabel, we should use the posted data
    if request.POST.get('formlabel', None) == key:
        # Get the form and initate it with the sent data
        form = form_holder.get(key).get('class')(
            data=request.POST
        )

        # Validate the form
        if form.is_valid():
            # Correct data entries
            messages.info(request, _(u"Configuration validée."))

            if form.save():
                # Save succeeded
                messages.success(
                    request,
                    _(u"Données enregistrées avec succès.")
                )
            else:
                # Save failed
                messages.warning(
                    request,
                    _(u"Un problème est survenu pendant l'enregistrement "
                      u"des données, merci de réessayer plus tard.")
                )
        else:
            # Form is not valid, show feedback to the user
            messages.error(
                request,
                _(u"Merci de corriger les erreurs suivantes.")
            )
    else:
        # Just initiate the form without data
        form = form_holder.get(key).get('class')(key)()

    # Add the attribute for the name
    setattr(form, 'formlabel', key)

    # Append it to the tempalte variable that will hold all the forms
    forms.append(form)

Tôi hy vọng điều này sẽ giúp trong tương lai.


2

Nếu bạn đang sử dụng phương pháp tiếp cận với các chế độ xem dựa trên lớp và các thành phần 'hành động' khác nhau, ý tôi là

Đặt các URL khác nhau trong hành động cho hai hình thức. Sau đó, bạn sẽ có hai chức năng xem khác nhau để đối phó với hai hình thức khác nhau.

Bạn có thể dễ dàng xử lý lỗi từ các hình thức khác nhau bằng cách sử dụng quá tải get_context_data phương pháp , ví dụ:

lượt xem:

class LoginView(FormView):
    form_class = AuthFormEdited
    success_url = '/'
    template_name = 'main/index.html'

    def dispatch(self, request, *args, **kwargs):
        return super(LoginView, self).dispatch(request, *args, **kwargs)

    ....

    def get_context_data(self, **kwargs):
        context = super(LoginView, self).get_context_data(**kwargs)
        context['login_view_in_action'] = True
        return context

class SignInView(FormView):
    form_class = SignInForm
    success_url = '/'
    template_name = 'main/index.html'

    def dispatch(self, request, *args, **kwargs):
        return super(SignInView, self).dispatch(request, *args, **kwargs)

    .....

    def get_context_data(self, **kwargs):
        context = super(SignInView, self).get_context_data(**kwargs)
        context['login_view_in_action'] = False
        return context

bản mẫu:

<div class="login-form">
<form action="/login/" method="post" role="form">
    {% csrf_token %}
    {% if login_view_in_action %}
        {% for e in form.non_field_errors %}
            <div class="alert alert-danger alert-dismissable">
                {{ e }}
                <a class="panel-close close" data-dismiss="alert">×</a>
            </div>
        {% endfor %}
    {% endif %}
    .....
    </form>
</div>

<div class="signin-form">
<form action="/registration/" method="post" role="form">
    {% csrf_token %}
    {% if not login_view_in_action %}
        {% for e in form.non_field_errors %}
            <div class="alert alert-danger alert-dismissable">
                {{ e }}
                <a class="panel-close close" data-dismiss="alert">×</a>
            </div>
        {% endfor %}
    {% endif %}
   ....
  </form>
</div>

2

lượt xem:

class AddProductView(generic.TemplateView):
template_name = 'manager/add_product.html'

    def get(self, request, *args, **kwargs):
    form = ProductForm(self.request.GET or None, prefix="sch")
    sub_form = ImageForm(self.request.GET or None, prefix="loc")
    context = super(AddProductView, self).get_context_data(**kwargs)
    context['form'] = form
    context['sub_form'] = sub_form
    return self.render_to_response(context)

def post(self, request, *args, **kwargs):
    form = ProductForm(request.POST,  prefix="sch")
    sub_form = ImageForm(request.POST, prefix="loc")
    ...

bản mẫu:

{% block container %}
<div class="container">
    <br/>
    <form action="{% url 'manager:add_product' %}" method="post">
        {% csrf_token %}
        {{ form.as_p }}
        {{ sub_form.as_p }}
        <p>
            <button type="submit">Submit</button>
        </p>
    </form>
</div>
{% endblock %}

4
Bạn có thể vui lòng giải thích câu trả lời của bạn? Nó sẽ giúp những người khác gặp vấn đề tương tự và có thể giúp gỡ lỗi mã của bạn hoặc người hỏi ...
creyD

0

Đây là cách đơn giản để xử lý các điều trên.

Trong mẫu Html, chúng tôi đặt bài

<form action="/useradd/addnewroute/" method="post" id="login-form">{% csrf_token %}

<!-- add details of form here-->
<form>
<form action="/useradd/addarea/" method="post" id="login-form">{% csrf_token %}

<!-- add details of form here-->

<form>

Trong chế độ xem

   def addnewroute(request):
      if request.method == "POST":
         # do something



  def addarea(request):
      if request.method == "POST":
         # do something

Trong URL Cung cấp thông tin cần thiết như

urlpatterns = patterns('',
url(r'^addnewroute/$', views.addnewroute, name='addnewroute'),
url(r'^addarea/', include('usermodules.urls')),
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.