【问题标题】:Django throws error when having multiple forms in formsets while making POST requestDjango 在发出 POST 请求时在表单集中有多个表单时抛出错误
【发布时间】:2019-04-08 09:10:52
【问题描述】:

我有一个带有仪表板和表单的 django 项目,其中包含在 django admin 中内置的表单集。我正在尝试从表单向 API 发出 POST 请求。我有两个具体的表格。当我在任一表单集中有多个表单时,我收到以下错误。然而,一个单一的形式正在经历。下面是我的代码和错误,

Internal Server Error: /admin/campaign/add
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py", line 126, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py", line 124, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/utils/decorators.py", line 142, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/views/decorators/cache.py", line 45, in _wrapped_view_func
    add_never_cache_headers(response)
  File "/usr/local/lib/python3.7/site-packages/django/utils/cache.py", line 252, in add_never_cache_headers
    patch_response_headers(response, cache_timeout=-1)
  File "/usr/local/lib/python3.7/site-packages/django/utils/cache.py", line 243, in patch_response_headers
    if not response.has_header('Expires'):
AttributeError: 'NoneType' object has no attribute 'has_header'

forms.py

class CampaignForm(forms.Form):
    consumer = forms.CharField(label="Consumer", max_length=200)
    startDate = forms.DateTimeField(label="Start Date",
        input_formats=['%d/%m/%Y %H:%M'])
    endDate = forms.DateTimeField(label="End Date",
        input_formats=['%d/%m/%Y %H:%M'])
    referreeCredits = forms.IntegerField(label="Referree Credits")
    referrerCredits = forms.IntegerField(label="Referrer Credits")
    maxReferreeCredits = forms.IntegerField(label="Max Referree Credits")
    maxReferrerCredits = forms.IntegerField(label="Max Referrer Credits")
    message = forms.CharField(label="Message", max_length=200)
    kramerTemplateId = forms.CharField(label="Kramer Template ID", max_length=200)
    paymentMode = forms.ChoiceField(label="Payment Mode", choices=[("PAYTM","PAYTM")])

class RuleForm(forms.Form):
    eventName = forms.CharField(label="Event Name", max_length=200)
    operator = forms.ChoiceField(label="Operator", choices=[("EQUAL","EQUAL"), ("EVERY","EVERY")])
    value = forms.IntegerField(label="Value")

class MilestoneRulesForm(forms.Form):
    operator = forms.ChoiceField(label="Operator", choices=[("EQUAL","EQUAL"), ("EVERY","EVERY")])
    value = forms.IntegerField(label="Value")
    referrerCredits = forms.IntegerField(label="Referrer Credits")

views.py

def campaign_add(self, request):
        form = CampaignForm()
        RuleFormSet = formset_factory(RuleForm)
        MilestoneFormSet = formset_factory(MilestoneRulesForm)
        if request.method == 'POST':
            # import ipdb; ipdb.set_trace()
            form = CampaignForm(request.POST)
            rule_formset = RuleFormSet(request.POST, prefix='rules')
            milestone_formset = MilestoneFormSet(request.POST, prefix='milestones')
            if form.is_valid() and rule_formset.is_valid() and milestone_formset.is_valid():
                    import ipdb; ipdb.set_trace()
                    dat = {}
                    dat["consumer"] = form.cleaned_data["consumer"]
                    dat["startDate"] = self.datetime_to_epoch(form.cleaned_data["startDate"])
                    dat["endDate"] = self.datetime_to_epoch(form.cleaned_data["endDate"])
                    dat["referreeCredits"] = form.cleaned_data["referreeCredits"]
                    dat["referrerCredits"] = form.cleaned_data["referrerCredits"]
                    dat["maxReferreeCredits"] = form.cleaned_data["maxReferreeCredits"]
                    dat["maxReferrerCredits"] = form.cleaned_data["maxReferrerCredits"]
                    dat["message"] = form.cleaned_data["message"]
                    dat["kramerTemplateId"] = form.cleaned_data["kramerTemplateId"]
                    dat["paymentMode"] = form.cleaned_data["paymentMode"]
                    arrRules = rule_formset.cleaned_data
                    arrMilestoneRules = milestone_formset.cleaned_data
                    dat["eventRules"] = arrRules
                    dat["milestoneRules"] = arrMilestoneRules
                    print(rule_formset.cleaned_data)
                    print(milestone_formset.cleaned_data)
                    res = requests.post("https://example.com", data=json.dumps(dat), headers={'content-type': 'application/json'})
                    if res.status_code == 201 or res.status_code == 200:
                        messages.success(request, 'Success!')
                        return redirect("admin:campaign-pg1")
                    else:
                        messages.error(request, 'Submission Failed.')
        else:
            rule_formset = RuleFormSet(prefix="rules")
            milestone_formset = MilestoneFormSet(prefix="milestones")
            return TemplateResponse(request, "admin/campaign_add.html", {"form":form, "rule_formset": rule_formset, "milestone_formset": milestone_formset})

HTML模板:

{% block content %}
    <div class="form_div" style="width:60%;">
        <h2>Campaign Form</h2>
        <form method="POST">
            {% csrf_token %}
            {% for field in form.visible_fields %}
                <div class="form-group">
                    {{ field.label_tag }}
                    {% render_field field class="form-control" %}
                    {% if field.help_text %}
                        <small class="form-text text-muted">{{ field.help_text }}</small>
                    {% endif %}
                </div>
            {% endfor %}            
            <div style="display: flex; justify-content: space-between; width: 280px;">
                <h3>Rules</h3>
                <button type="button" id="generate-rule" class="btn btn-primary">+</button>
            </div>
            <div class="parent">
            <div>
            {{ rule_formset.management_form }}
            {% for ruleForm in rule_formset %}
                {{ ruleForm }}
                <input type="button"  class="btn btn-warn" style="background: red;" value="-">
            {% endfor %}
            </div>
            </div>
             <div style="display: flex; justify-content: space-between; width: 280px;">
                <h3>Milestone Rules</h3>
                <button  type="button" id="generate-milestone-rule" class="btn btn-primary">+</button>
            </div>
            <div class="milestone-parent">
            <div>
            {{ milestone_formset.management_form }}
            {% for milestoneForm in milestone_formset %}
                {{ milestoneForm }}
                <input type="button"  class="btn btn-warn" style="background: red;" value="-">
            {% endfor %}
            </div>
            </div>
            <div>
            <input type="submit" value="Post">
            </div>
        </form>
    </div>

    <script>
        $(function () {
            $("#id_startDate").datetimepicker({
            format: 'd/m/Y H:i',
            });
            $("#id_endDate").datetimepicker({
            format: 'd/m/Y H:i',
            });
        });
    </script>

    <script type='text/javascript'>

        const ruleform = `
        <div>
            <label for="id_rules-{n}-eventName">Event Name:</label>
            <input type="text" name="rules-{n}-eventName" maxlength="200" id="id_rules-{n}-eventName">

            <label for="id_rules-{n}-operator">Operator:</label>
            <select name="rules-{n}-operator" id="id_rules-{n}-operator">
                <option value="EQUAL">EQUAL</option>
                <option value="EVERY">EVERY</option>
            </select>

            <label for="id_rules-{n}-value">Value:</label>
            <input type="number" name="rules-{n}-value" id="id_rules-{n}-value">
            <input type="button" class="btn btn-warn" style="background: red;" value="-">
        </div>
        `
        const milestoneform = `
        <div>
            <label for="id_milestones-{n}-operator">Operator:</label>
            <select name="milestones-{n}-operator" id="id_milestones-{n}-operator">
                <option value="EQUAL">EQUAL</option>
                <option value="EVERY">EVERY</option>
            </select>

            <label for="id_milestones-{n}-value">Value:</label>
            <input type="number" name="milestones-{n}-value" id="id_milestones-{n}-value">

            <label for="id_milestones-{n}-mreferrerCredits">Referrer Credits:</label>
            <input type="number" name="milestones-{n}-mReferrerCredits" maxlength="200" id="id_milestones-{n}-mReferrerCredits">

            <input type="button" class="btn btn-warn" style="background: red;" value="-">
        </div>
        `
        $("#generate-rule").on('click', function(e){
            var n = $("#id_rules-TOTAL_FORMS").val(); // current number of forms;
            var new_form = ruleform.replace(/{n}/g, n); // replace all {n} by the new form number
            $(".parent").append(new_form);
            $("#id_rules-TOTAL_FORMS").val(parseInt(n) + 1); // update the total forms number
            console.log($("#id_rules-TOTAL_FORMS").val());
        })
        $("#generate-milestone-rule").on('click', function(e){
             var n = $("#id_milestones-TOTAL_FORMS").val(); // current number of forms;
            var new_form = milestoneform.replace(/{n}/g, n); // replace all {n} by the new form number
            $(".milestone-parent").append(new_form);
            $("#id_milestones-TOTAL_FORMS").val(parseInt(n) + 1); // update the total forms number
            console.log($("#id_milestones-TOTAL_FORMS").val());
        })
        $(".parent").on("click", (e) => {
            //n = $("#id_rules-TOTAL_FORMS").val();
            //$("#id_rules-TOTAL_FORMS").val(parseInt(n)-1);
            //console.log($("#id_rules-TOTAL_FORMS").val());
            return e.target.classList.contains('btn-warn') && e.target.parentNode.remove()
        })

        $(".milestone-parent").on("click", (e) => {
            //n = $("#id_milestones-TOTAL_FORMS").val();
            //$("#id_milestones-TOTAL_FORMS").val(parseInt(n)-1);
            //console.log($("#id_milestones-TOTAL_FORMS").val());
            return e.target.classList.contains('btn-warn') && e.target.parentNode.remove()
        })
    </script>
{% endblock %}

如何解决此错误,以便我可以在我的表单集中使用任意数量的表单发出 POST 请求?

编辑: 这就是我的表单集的样子。当我有多个表格时,我会收到上述错误。 my formsets

【问题讨论】:

    标签: django django-forms


    【解决方案1】:

    可能是因为您的请求状态码无效(不是 200 或 201)吗?在这种情况下,您不会返回您只是调用的任何响应:

    else:
        messages.error(request, 'Submission Failed.')
    

    您的异常的堆栈跟踪表明没有响应对象(您没有从视图返回任何响应)

    File "/usr/local/lib/python3.7/site-packages/django/utils/cache.py", line 243, in patch_response_headers
        if not response.has_header('Expires'):
    AttributeError: 'NoneType' object has no attribute 'has_header'
    

    检查 api 调用失败的原因并解决问题,然后您应该能够提交表单。

    【讨论】:

    • 嗨。但是,为什么我在规则和里程碑规则中有多个表单时会出现错误,而当我只有一个表单时请求正在处理。
    • 您的大部分表单字段似乎都是必填字段。如果您有多个表单,则可能会提交无法通过验证的空表单。找出答案的唯一方法是查看您的所有 formset['errors'] 属性(使用 pdb 或使用 print 语句)以及您的 form['errors'] 属性
    【解决方案2】:

    默认情况下,formset_factory() 定义了一个额外的表单。显示的空表单的数量由 extra 参数控制。

    MilestoneFormSet = formset_factory(MilestoneForm, extra=2)
    RuleFormSet = formset_factory(RuleForm, extra=2)
    

    【讨论】:

      猜你喜欢
      • 2021-02-07
      • 2022-01-06
      • 2022-01-23
      • 2018-10-11
      • 1970-01-01
      • 1970-01-01
      • 2021-04-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多