Django의 한 페이지에서 여러 양식을 처리하는 올바른 방법


200

두 가지 형식이 필요한 템플릿 페이지가 있습니다. 하나의 양식 만 사용하면이 전형적인 예와 같이 문제가 없습니다.

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

그러나 여러 양식으로 작업하려면 양식 중 하나만 제출하고 다른 양식은 제출하지 않는다는 것을 어떻게 알 수 있습니까? 일어난)?


이것은 예상 문구 와 금지 문구 가 다른 양식에 대한 제출 버튼의 이름이고 예상 문구 양식 과 금지 문구 양식이 양식 인 답변을 기반으로 한 솔루션 입니다.

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
솔루션에 논리적 오류가 없습니까? '금지 문구'를 게시하면 예상 문구 양식이 채워지지 않습니다.
— Ztyx

2
이 문제는 동시에 여러 형태의 처리에 관한, 한 번에 하나의 양식을 처리 할 수
— 빛나는

답변:


140

몇 가지 옵션이 있습니다.

  1. 두 양식에 대해 다른 URL을 조치에 넣으십시오. 그런 다음 두 가지 다른 양식을 처리하기 위해 두 가지 다른보기 기능이 있습니다.

  2. POST 데이터에서 제출 단추 값을 읽으십시오. 어떤 제출 버튼이 클릭되었는지 알 수 있습니다. 여러 제출 버튼을 어떻게 만들 수 있습니까?


5
3) POST 데이터의 필드 이름에서 제출 된 양식을 결정하십시오. 보낸 사람에 가능한 모든 값이 비어 있지 않은 고유 필드가없는 경우 숨겨진 입력을 포함하십시오.
— Denis Otkidach

13
4) 양식을 식별하는 숨겨진 필드를 추가하고보기에서이 필드의 값을 확인하십시오.
— Soviut

가능한 경우 POST 데이터를 오염시키지 마십시오. 대신 양식 작업 URL에 GET 매개 변수를 추가하는 것이 좋습니다.
— pygeek

6
# 1은 여기서 가장 좋은 방법입니다. 숨겨진 필드로 POST를 오염시키고 싶지 않으며 뷰를 템플릿 및 / 또는 양식에 묶고 싶지도 않습니다.
— meteorainer

5
@meteorainer 1을 사용하는 경우 메시지 프레임 워크 또는 쿼리 문자열을 사용하지 않고 오류를 인스턴스화하는 상위보기의 양식으로 오류를 다시 전달할 수 있습니까? 이 답변에 가장 가까운 것, 그러나 여기 여전히 두 폼을 처리 한 뷰의 : stackoverflow.com/a/21271659/2532070
— YPCrumble

45

나중에 참조 할 수있는 방법은 다음과 같습니다. bannedphraseform이 첫 번째 양식이고 예상 문구가 두 번째 양식입니다. 첫 번째 것이 맞으면 두 번째 것은 건너 뜁니다 (이 경우 합리적인 가정).

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
prefix =를 사용하는 것은 실제로 '적절한 방법'입니다
— Rich

prefix-kwarg가 일을 잘했습니다!
— Stephan Hoyer

1
그 접두사를 가진 좋은 아이디어, 우리는 지금 그것들을 사용했고 그것들은 매력처럼 작동합니다. 그러나 두 양식이 모두 라이트 박스 (각각 별도의 양식)에 있기 때문에 제출 된 양식을 감지하기 위해 숨겨진 필드를 삽입해야했습니다. 올바른 라이트 박스를 다시 열어야하므로 제출 된 양식을 정확히 알아야합니다. 그런 다음 첫 번째 양식에 유효성 검사 오류가있는 경우 두 번째 양식이 자동으로 승리하고 첫 번째 양식이 재설정됩니다. 첫 번째 형태. 당신이 알아야 할 생각
— Enduriel

이 패턴을 세 가지 형태로 확장하는 것이 번거롭지 않습니까? 마찬가지로 첫 번째 양식에서 is_valid ()를 확인한 다음 첫 번째 양식 등 handled = False을 확인 True하면 호환되는 양식을 찾을 때 업데이트 됩니다.
— binki

14

Django의 클래스 기반 뷰는 일반적인 FormView를 제공하지만 모든 의도와 목적을 위해 하나의 양식 만 처리하도록 설계되었습니다.

Django의 일반 뷰를 사용하여 동일한 대상 작업 URL로 여러 양식을 처리하는 한 가지 방법은 아래에 표시된 것처럼 'TemplateView'를 확장하는 것입니다. 이 방법을 자주 사용하여 Eclipse IDE 템플릿으로 만들었습니다.

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)

html 템플릿은 다음과 같은 효과가 있습니다.

...

<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
나는이 같은 문제로 어려움을 겪고 있으며 각 게시물을 별도의 양식보기로 처리 한 다음 공통 템플릿보기로 리디렉션하는 방법을 찾으려고했습니다. 요점은 템플릿보기가 컨텐츠 가져 오기와 저장을위한 양식보기를 담당하게하는 것입니다. 그래도 유효성 검사는 문제입니다. 세션에 양식을 저장하면 내 마음이 넘어갔습니다 ... 여전히 깨끗한 해결책을 찾고 있습니다.
— Daniele Bernardini

14

같은 페이지에서 독립적으로 검증 된 여러 양식이 필요했습니다. 내가 빠진 주요 개념은 1) 제출 버튼 이름에 양식 접두사를 사용하고 2) 무제한 양식이 유효성 검사를 트리거하지 않는다는 것입니다. 다른 사람에게 도움이된다면 @ adam-nelson 및 @ daniel-sokolowski의 답변과 @zeraien의 의견 ( https : //.com/a/17303480 / 2680349 ) :

# 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>

나는 이것이 실제로 깨끗한 해결책이라고 생각합니다. 감사.
— chhantyal

나는이 솔루션을 정말 좋아한다. 한 가지 질문 : _get_form ()이 MyView 클래스의 메소드가 아닌 이유가 있습니까?
— 공습

1
@ AndréTerra 확실히 그렇게 할 수는 있지만 다른 뷰에서 재사용 할 수 있도록 TemplateView에서 상속되는 일반 클래스에 포함하고 싶을 것입니다.
— ybendana

1
이것은 훌륭한 솔루션입니다. 작동하도록 __get_form의 한 줄을 변경해야했습니다. data = request.POST if prefix in next(iter(request.POST.keys())) else None 그렇지 않으면 in작동하지 않았습니다.
— larapsodia

이와 같이 단일 <form> 태그를 사용하면 어떤 제출 단추를 클릭했는지에 따라 양식별로 작성해야하는 필수 필드가 전역 적으로 필요합니다. 동일한 동작으로 두 개의 <form> 태그로 분할하면 작동합니다.
— 플래시

3

Django Forms를 사용하지 않는 솔루션을 공유하고 싶었습니다. 단일 페이지에 여러 양식 요소가 있고 단일보기를 사용하여 모든 양식의 모든 POST 요청을 관리하려고합니다.

내가 한 것은 보이지 않는 입력 태그를 도입하여 매개 변수를 뷰에 전달하여 제출 된 양식을 확인할 수 있다는 것입니다.

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

views.py

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

나는 이것이 좋고 쉬운 탈출구라고 생각한다
— Shedrack

2

이것은 조금 늦었지만 이것이 내가 찾은 최고의 솔루션입니다. 양식 이름 및 클래스에 대한 조회 사전을 작성하고 양식을 식별하기위한 속성을 추가해야하며보기에서을 사용하여 숨겨진 필드로 추가해야합니다 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)

앞으로 도움이 되길 바랍니다.


2

클래스 기반보기와 다른 '액션'속성으로 접근 방식을 사용하는 경우

두 양식에 대해 다른 URL을 조치에 넣으십시오. 그런 다음 두 가지 다른 양식을 처리하기 위해 두 가지 다른보기 기능이 있습니다.

다음과 같이 오버로드 된 get_context_data방법을 사용하여 다른 형식의 오류를 쉽게 처리 할 수 ​​있습니다 .

views.py :

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

주형:

<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

전망:

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")
    ...

주형:

{% 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
답을 설명해 주시겠습니까? 그것은
— 비슷한

0

위를 처리하는 간단한 방법이 있습니다.

HTML 템플릿에서 Post를 넣습니다.

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

보기에서

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



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

URL에서 필요한 정보 제공

urlpatterns = patterns('',
url(r'^addnewroute/$', views.addnewroute, name='addnewroute'),
url(r'^addarea/', include('usermodules.urls')),
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.