วิธีที่เหมาะสมในการจัดการหลายรูปแบบในหน้าเดียวใน Django


201

ฉันมีหน้าเทมเพลตที่ต้องการรูปแบบสองแบบ ถ้าฉันใช้เพียงรูปแบบเดียวสิ่งต่าง ๆ ก็ดีเหมือนในตัวอย่างทั่วไปนี้:

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

ถ้าฉันต้องการทำงานกับหลาย ๆ รูปแบบฉันจะให้มุมมองรู้ได้อย่างไรว่าฉันส่งแบบฟอร์มเพียงแบบเดียวและไม่ใช่แบบอื่น ๆ (เช่นมันยังคงเป็นคำขอ POST แต่ฉันต้องการประมวลผลแบบฟอร์มที่ส่งเท่านั้น ที่เกิดขึ้น)?


นี้เป็นวิธีการขึ้นอยู่กับคำตอบที่expectedphraseและbannedphraseเป็นชื่อส่งปุ่มสำหรับรูปแบบที่แตกต่างกันและexpectedphraseformและbannedphraseformจะรูปแบบ

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
สิ่งนี้จะจัดการแบบฟอร์มเพียงครั้งเดียวคำถามคือเกี่ยวกับการจัดการหลายรูปแบบในเวลาเดียวกัน
ส่อง

คำตอบ:


141

คุณมีตัวเลือกน้อย:

  1. ใส่ URL ที่แตกต่างกันในการดำเนินการสำหรับสองรูปแบบ จากนั้นคุณจะมีฟังก์ชั่นการดูที่แตกต่างกันสองแบบเพื่อจัดการกับทั้งสองรูปแบบที่แตกต่างกัน

  2. อ่านค่าปุ่มส่งจากข้อมูล POST คุณสามารถบอกได้ว่าคลิกปุ่มส่งใด: ฉันจะสร้างฟอร์มส่งปุ่ม django ได้อย่างไร?


5
3) กำหนดว่าจะส่งฟอร์มใดจากชื่อฟิลด์ในข้อมูล POST รวมอินพุตที่ซ่อนไว้บางส่วนหากตัวอักษรของคุณไม่มีฟิลด์ที่ไม่ซ้ำกันและค่าที่เป็นไปได้ทั้งหมดไม่ว่าง
เดนิส Otkidach

13
4) เพิ่มฟิลด์ที่ซ่อนอยู่เพื่อระบุแบบฟอร์มและตรวจสอบค่าของฟิลด์นี้ในมุมมองของคุณ
Soviut

ฉันจะอยู่ห่างจากมลพิษข้อมูล POST ถ้าเป็นไปได้ ฉันขอแนะนำให้เพิ่มพารามิเตอร์ GET เข้ากับ URL การดำเนินการของแบบฟอร์มแทน
pygeek

6
# 1 เป็นทางออกที่ดีที่สุดของคุณที่นี่ คุณไม่ต้องการสร้างมลภาวะ POST ด้วยฟิลด์ที่ซ่อนอยู่และไม่ต้องการโยงมุมมองไปยังเทมเพลตและ / หรือแบบฟอร์ม
อุกกาบาต

5
@meteorainer ถ้าคุณใช้หมายเลขหนึ่งมีวิธีส่งข้อผิดพลาดกลับไปที่ฟอร์มในมุมมองพาเรนต์ที่สร้างอินสแตนซ์เหล่านี้โดยไม่ใช้เฟรมเวิร์กข้อความหรือสตริงการสืบค้นหรือไม่ คำตอบนี้ดูเหมือนจะใกล้เคียงที่สุด แต่ที่นี่ยังมีเพียงหนึ่งมุมมองที่จัดการกับทั้งสองรูปแบบ: stackoverflow.com/a/21271659/2532070
YPCrumble

45

วิธีการอ้างอิงในอนาคตมีลักษณะเช่นนี้ bannedphraseform เป็นรูปแบบแรกและคาดว่า phraseform เป็นรูปแบบที่สอง หากมีการเข้าชมรายการแรกรายการที่สองจะถูกข้าม (ซึ่งเป็นข้อสมมติที่สมเหตุสมผลในกรณีนี้):

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
ใช้คำนำหน้า = แน่นอน 'วิธีที่เหมาะสม'
Rich

คำนำหน้า -kwarg ทำงานได้ดี!
เตฟานฮอยเออร์

1
แนวคิดที่ยอดเยี่ยมสำหรับคำนำหน้าเหล่านั้นเราใช้มันในตอนนี้และพวกมันทำงานได้อย่างมีเสน่ห์ แต่เรายังต้องแทรกเขตข้อมูลที่ซ่อนอยู่เพื่อตรวจสอบว่ามีการส่งแบบฟอร์มใดเนื่องจากทั้งสองรูปแบบนั้นอยู่ใน Lightbox (แต่ละแบบแยกกัน) เนื่องจากเราจำเป็นต้องเปิดไลท์บ็อกซ์ที่ถูกต้องอีกครั้งเราจำเป็นต้องทราบว่าแบบฟอร์มใดถูกส่งมาและถ้าหากรูปแบบแรกมีข้อผิดพลาดในการตรวจสอบความถูกต้องที่สองชนะโดยอัตโนมัติและรูปแบบแรกจะถูกตั้งค่าใหม่ แบบฟอร์มแรก แค่คิดว่าคุณควรรู้
Enduriel

มันจะไม่เป็นไรหรือที่จะขยายรูปแบบนี้ไปยังกรณีของสามรูปแบบ? เช่นเดียวกับการตรวจสอบ is_valid () จากแบบฟอร์มแรกจากนั้นสองคนแรกเป็นต้น…อาจมีเพียงhandled = Falseที่ได้รับการปรับปรุงTrueเมื่อพบรูปแบบที่เข้ากันได้หรือไม่
binki

14

มุมมองที่อิงตามคลาสของ Django นั้นให้ FormView ทั่วไป แต่สำหรับทุกเจตนาและวัตถุประสงค์มันถูกออกแบบมาเพื่อจัดการรูปแบบเดียวเท่านั้น

วิธีหนึ่งในการจัดการหลายรูปแบบด้วย url การกระทำเป้าหมายเดียวกันโดยใช้มุมมองทั่วไปของ Django คือการขยาย '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) รูปแบบที่ไม่ได้ จำกัด การเรียกใช้การตรวจสอบ ถ้ามันช่วยคนอื่นนี่คือตัวอย่างที่ง่ายของฉันของสองฟอร์ม AForm และ BForm โดยใช้ TemplateView ตามคำตอบโดย @ adam-nelson และ @ daniel-sokolowski และแสดงความคิดเห็นโดย @zeraien ( https://stackoverflow.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
airstrike

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> (ด้วยการกระทำเดียวกัน) ใช้งานได้
Flash

3

ต้องการแบ่งปันวิธีการแก้ปัญหาของฉันที่ไม่ได้ใช้แบบฟอร์ม Django ฉันมีองค์ประกอบหลายรูปแบบในหน้าเดียวและฉันต้องการใช้มุมมองเดียวเพื่อจัดการคำขอ 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
คุณช่วยอธิบายคำตอบของคุณได้ไหม มันจะช่วยให้ผู้อื่นที่มีปัญหาคล้ายกันและจะช่วยให้การแก้จุดบกพร่องหรือรหัสคนถาม ...
creyD

0

นี่คือวิธีง่ายๆในการจัดการกับด้านบน

ในแม่แบบ Html เราใส่โพสต์

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