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.