Django 表单字段动态显示

Posted

技术标签:

【中文标题】Django 表单字段动态显示【英文标题】:Django Form Fields Appear Dynamically 【发布时间】:2019-05-02 20:47:42 【问题描述】:

我的 Django 应用程序中有一个包含超过 20 个字段的表单。有人要求我只显示前几个字段。填写完这些字段后,除了前面的字段外,还应显示接下来的几个字段。我怎样才能做到这一点?

以下是我的forms.py

class QuoteForm(forms.Form):
    premium_station = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Premium Admin Stations Needed'))
    standard_station = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Standard Admin Stations Needed'))
    basic_station = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Basic Admin Stations Needed'))
    messaging_station = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Messaging Stations Needed'))
    auto_attendant = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Auto Attendants Needed'))
    toll_service = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Toll-Free Services Needed'))
    receptionist = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Receptionist Clients Needed'))
    group_paging = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Group Paging Needed'))
    FourG_backup = forms.IntegerField(max_value=2000, min_value=0,  required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# 4G Backups Needed'))
    broadsoft_hub = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# BroadSoft Hubs Needed'))
    polycom_410 = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Polycom VVX 410 Needed'))
    spa_122 = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Linksys SPA122 Needed'))
    yealink = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Yealink W52P POE Needed'))

我的观点.py:

def quote(request, phonenumber):
    if request.method=='POST':
        quote = PBXQuote.objects.filter(phone_number=phonenumber).order_by('id').last()
        quote.premium_station = request.POST.get('premium_station')
        quote.standard_station = request.POST.get('standard_station')
        quote.basic_station = request.POST.get('basic_station')
        quote.messaging_station = request.POST.get('messaging_station')
        quote.auto_attendant = request.POST.get('auto_attendant')
        quote.hunt_group = request.POST.get('hunt_group')
        quote.toll_service = request.POST.get('toll_service')
        quote.music_hold = request.POST.get('music_hold')
        quote.call_recording = request.POST.get('call_recording')
        quote.receptionist = request.POST.get('receptionist')
        quote.group_paging = request.POST.get('group_paging')
        quote.FourG_backup = request.POST.get('FourG_backup')
        quote.broadsoft_hub = request.POST.get('broadsoft_hub')
        quote.polycom_410 = request.POST.get('polycom_410')
        quote.spa_122 = request.POST.get('spa_122')
        quote.yealink = request.POST.get('yealink')
        quote.ported_nums = request.POST.get('ported_nums')
        quote.new_nums = request.POST.get('new_nums')
        quote.directory_listing = request.POST.get('directory_listing')
        quote.save()
        return redirect('../../viewpbxquote//'.format(phonenumber))


    else:
        form = PBXQuoteForm()
    context = 'form' : form
    return render(request, 'Home/pbxquote.html', context)

我的 HTML 文件中的表单部分:

            <section id="quote-section">
                    <div id="order-form-container">
                            <form class="login" method="post">
                                    % csrf_token %
                                    <h1>Quote</h1>
                                    <h2>Hosted Stations</h2>
                                    <div class="form-group">
                                            <label for="premium_station" class="sr-only">Premium Stations</label>
                                             form.premium_station  Monthly: $32.00 | One-Time: $8.00 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="standard_station" class="sr-only">Standard Stations</label>
                                             form.standard_station  Monthly: $25.00 | One-Time: $8.00 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="basic_station" class="sr-only">Basic Stations</label>
                                             form.basic_station  Monthly: $14.00 | One-Time: $8.00 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="messaging_station" class="sr-only">Messaging Stations</label>
                                             form.messaging_station  Monthly: $4.95 | One-Time: N/A Each
                                    </div><!--Ideally, this would be where the first displayed section would end. As long as at least one of these values changes, the next section would appear, as it shouldn't be necessary for every field unwanted to be set to '0'.-->
                                    <h2>Services</h2>
                                    <div class="form-group">
                                            <label for="auto_attendant" class="sr-only">Auto Attendants</label>
                                    <div class="form-group">
                                            <label for="hunt_group" class="sr-only">Hunt Group Package</label>
                                            <select class="form-control" name="hunt-group">
                                                    <option disabled selected>Hunt Group</option>
                                                    <option value="1">Yes</option>
                                                    <option value="0">No</option>
                                            </select>
                                            <span>Monthly: N/A | One-Time: N/A</span>
                                    </div>
                                    <div class="form-group">
                                            <label for="toll_service" class="sr-only">Toll-Free Services</label>
                                             form.toll_service  Monthly: $4.95 | One-Time: $14.95 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="music_hold" class="sr-only">Music On Hold</label>
                                            <select class="form-control" name="music_hold">
                                                    <option disabled selected>Music On Hold</option>
                                                    <option value="1">Yes</option>
                                                    <option value="0">No</option>
                                            </select>
                                            <span>Monthly: $9.95 | One-Time: N/A </span>
                                    </div>
                                    <div class="form-group">
                                            <label for="call_recording" class="sr-only">
                                            <select class="form-control" class="sr-only">
                                                    <option disabled selected>Call Recording</option>
                                                    <option value="1">Yes</option>
                                                    <option value="0">No</option>
                                            </select>
                                            <span>Monthly: $9.95 | One-Time: N/A </span>
                                    </div>
                                    <div class="form-group">
                                            <label for="receptionist" class="sr-only">Receptionist Clients</label>
                                             form.receptionist  Monthly: $9.95 | One-Time: $14.95 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="group_paging" class="sr-only">Group Paging</label>
                                             form.group_paging  Monthly: $9.95 | One-Time: $14.95 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="FourG_backup" class="sr-only">4G LTE Backup</label>
                                             form.FourG_backup  Monthly: $75.00 | One-Time: N/A Each
                                    </div>
                                    <div class="form-group">
                                            <label for="broadsoft_hub" class="sr-only">BroadSoft Hub</label>
                                             form.broadsoft_hub  Monthly: $2.00 | One-Time: N/A Each
                                    </div><!--The second section would end here. This time, every field that is a select would either be selected "Yes" or "No", and the next section would appear.-->
                                    <h2>Equipment</h2>
                                    <div class="form-group">
                                    <div class="form-group">
                                            <label for="polycom_410" class="sr-only">Polycom VVX 410</label>
                                             form.polycom_410  One-Time: $144.00 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="spa_122" class="sr-only">Linksys SPA-122 ATA</label>
                                             form.spa_122  One-Time: $34.90 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="yealink" class="sr-only">Yealink W52P</label>
                                             form.yealink  One-Time: $129.00 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="ported_nums" class="sr-only">Ported Numbers</label>
                                             form.ported_nums  Monthly: N/A | One-Time: $9.95 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="new_nums" class="sr-only">New Numbers</label>
                                             form.new_nums  Monthly: $1.00 | One-Time: $1.95 Each
                                    </div>
                                    <div class="form-group">
                                            <label for="directory_listing" class="sr-only">Directory Listings</label>
                                             form.directory_listing  Monthly: $10.00 | One-Time: $20.00 Each
                                    </div>
                                    </div>
                                    <button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button>
                            </form>
                    </div>
            </section>

我将非常感谢在此问题上提供的任何帮助,因为据我所知,我在网上没有找到任何可以完成我想做的事情的东西。

【问题讨论】:

【参考方案1】:

您可以有两种形式,一种用于前半场,另一种用于其余字段。然后,您只需控制要传递给模板的表单。 没做过,不过好像不太难。 另一种选择是使用 javascript 隐藏字段,直到前半部分被填充,然后取消隐藏第二部分,并隐藏第一部分。

【讨论】:

嗯...我觉得这看起来不错。我想我可能会使用 JavaScript 选项,因为我需要用户提交另一个发布请求以将表单的上下文更新到模板。此外,这将涉及重写所有文件,而不仅仅是添加 JavaScript。谢谢! 回想起来,我不确定 JavaScript 选项是否会起作用。使用 引用除选择标记之外的任何字段(我忘记了语言的具体名称),我试图找到一个函数来检查该字段的值是否已更新。我发现的唯一事情是检查提交新 POST 请求时是否与初始值有差异,而我只想在更改某些答案时显示字段,而无需提交另一个 POST 请求。【参考方案2】:

您可以使用表单向导来完成此操作。它创建了完全符合您需要的简洁表单,并且全部使用 Django 和 python,无需 javascript。

首先将 formtools 添加到已安装的应用中。

创建多个表单,而不仅仅是一个:

class QuoteForm1(forms.Form):
    premium_station = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Premium Admin Stations Needed'))
    standard_station = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Standard Admin Stations Needed'))
    basic_station = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Basic Admin Stations Needed'))

class QuoteForm2(forms.Form):
    messaging_station = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Messaging Stations Needed'))
    auto_attendant = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Auto Attendants Needed'))
    toll_service = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Toll-Free Services Needed'))

class QuoteForm3(forms.Form):
    receptionist = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Receptionist Clients Needed'))
    group_paging = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Group Paging Needed'))
    FourG_backup = forms.IntegerField(max_value=2000, min_value=0,  required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# 4G Backups Needed'))
    broadsoft_hub = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# BroadSoft Hubs Needed'))

class QuoteForm4(forms.Form):
    polycom_410 = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Polycom VVX 410 Needed'))
    spa_122 = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Linksys SPA122 Needed'))
    yealink = forms.IntegerField(max_value=2000, min_value=0, required=False, widget = forms.NumberInput(attrs='class' : 'form-control', 'placeholder' : '# Yealink W52P POE Needed'))

然后在您的视图中从 SessionWizardView 导入,并指定表单:

from formtools.wizard.views import SessionWizardView

class QuoteFormView(SessionWizardView):
    instance_dict = None
    form_list = [QuoteForm1, QuoteForm2, QuoteForm3, QuoteForm4]
    template_name = 'quote_form.html'

这样您可以为每个表单使用一个模板,但如果您需要为每个表单使用不同的模板:

TEMPLATES = 
            '0': 'quote_form1.html',
            '1': 'quote_form2.html',
            '2': 'quote_form3.html',
            '3': 'quote_form4.html',
            

将 get_template_names 添加到您的视图中:

def get_template_names(self):
    return [TEMPLATES[self.steps.current]]

您使用 done 方法来保存表单,而不是通常的 form.is_valid:

def done(self, form_list, **kwargs):
    data = k: v for form in form_list for k, v in form.cleaned_data.items()
    # Reference your data 
    attribute = data['premium_station']
    return HttpResponseRedirect('/redirect/')

并且在你的模板中你必须有 management_form 否则它将不起作用:

% extends "base.html" %
% load i18n %

% block head %
 wizard.form.media 
% endblock %

% block content %
<p>Step  wizard.steps.step1  of  wizard.steps.count </p>
<form action="" method="post">% csrf_token %
<table>
 wizard.management_form 
% if wizard.form.forms %
     wizard.form.management_form 
    % for form in wizard.form.forms %
         form 
    % endfor %
% else %
     wizard.form 
% endif %
</table>
% if wizard.steps.prev %
<button name="wizard_goto_step" type="submit" value=" wizard.steps.first ">% trans "first step" %</button>
<button name="wizard_goto_step" type="submit" value=" wizard.steps.prev ">% trans "prev step" %</button>
% endif %
<input type="submit" value="% trans "submit" %"/>
</form>
% endblock %

供参考

https://django-formtools.readthedocs.io/en/latest/wizard.html#

【讨论】:

哇,太不可思议了!我想我可能会使用类似的东西!非常感谢! :)

以上是关于Django 表单字段动态显示的主要内容,如果未能解决你的问题,请参考以下文章

drupal:表单 API,根据输入动态隐藏或显示字段

后台传回的Json数据怎么在HTML表单中显示并能动态编辑(添加、删除)

django - 动态添加 django 表单字段并保留用户输入

django 模板:如何知道表单字段类型并根据字段类型添加任何按钮

如何在渲染django模板时动态设置表单选择字段的初始值

django - 表单中的动态选择字段