正确使用modelformset_factory以使用formset

时间:2019-02-02 17:37:47

标签: python django django-forms formset

问题描述: 我正在构建一个表单集,以显示我的模型中的所有数据并进行编辑。我的视图已正确渲染,并显示了保存在模型中的正确数据。但是在POST上,我遇到一个异常,指出“ ManagementForm数据丢失或已被篡改”。我想知道为什么会这样。完整的代码和跟踪信息如下。

我的模型:

class ProcedureTemplate(models.Model):
    templid = models.AutoField(primary_key=True, unique=True)
    title = models.CharField(max_length=200)
    description = models.CharField(max_length=5000, default='', blank=True)
    clinic = models.ForeignKey(Clinic, on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.description}'

class SectionHeading(models.Model):
    procid = models.AutoField(primary_key=True, unique=True)
    name = models.CharField(max_length=200)
    default = models.CharField(max_length=1000)
    sortorder = models.IntegerField(default=1000)

    fieldtype_choice = (
        ('heading1', 'Heading1'),
        ('heading2', 'Heading2'),
        )
    fieldtype = models.CharField(
        choices=fieldtype_choice, max_length=100, default='heading1')

    template = models.ForeignKey(ProcedureTemplate, on_delete=models.CASCADE, null=False)

    def __str__(self):
        return f'{self.name} [{self.procid}]'

我的表单:

class ProcedureCrMetaForm(ModelForm):
    class Meta:
        model = SectionHeading
        fields = [
            'name',
            'default',
            'sortorder',
            'fieldtype'
        ]
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['name'].widget.attrs.update({'class': 'form-control'})
        self.fields['default'].widget.attrs.update({'class': 'form-control'})
        self.fields['sortorder'].widget.attrs.update({'class': 'form-control'})
        self.fields['fieldtype'].widget.attrs.update({'class': 'form-control'})

ProcedureCreationFormset = formset_factory(ProcedureCrMetaForm, extra=3)
ProcedureModificationFormset = modelformset_factory(SectionHeading, ProcedureCrMetaForm,
    fields=('name', 'default', 'sortorder','fieldtype'),
    # widgets={"name": Textarea()}
    )

我的观点:

def procedure_template_modification_alt(request, cliniclabel, template_id):
    msg = ''
    clinicobj = Clinic.objects.get(label=cliniclabel)
    template_id = int(template_id)
    template= ProcedureTemplate.objects.get(templid = template_id)

    formset = ProcedureModificationFormset(queryset=SectionHeading.objects.filter(template = template))


    if request.method == 'POST':
        print(request.POST.get)
        # Create a formset instance with POST data.
        formset = ProcedureModificationFormset(request.POST)
        if formset.is_valid():
            print("Form is valid")
        else:
            print("Form is invalid")
        # Assuming all is valid, save the data.
        instances = formset.save()
        print(instances)

    template= ProcedureTemplate.objects.get(templid = template_id)
    headings = SectionHeading.objects.filter(template = template)

    return render(request, 'procedures/create_procedure_formset_alt.html',
    {
        'template': template,
        'formset': formset,
        'headings': headings,
        'msg': msg,
        'rnd_num': randomnumber(),
    })

我的模板:

{% block content %}
{% load widget_tweaks %}
<div class="container">
    {% if user.is_authenticated %}
    <div class="row my-1">
            <div class="col-sm-2">Name</div>
            <div class="col-sm-22">
                <input type="text" name="procedurename" class="form-control" placeholder="Enter name of procedure (E.g. DNE)" value="{{ template.title }}" />
            </div>
    </div>
    <div class="row my-1">
        <div class="col-sm-2">Description</div>
        <div class="col-sm-22">
            <input type="text" name="proceduredesc" class="form-control" placeholder="Enter description of procedure (E.g. Diagnostic Nasal Endoscopy)" value="{{ template.description }}" />
        </div>
    </div>
    <form action="" method="post" enctype="multipart/form-data">
        {% csrf_token %}        
        <div class="row mt-3">
            <div class="col-sm-1">Select</div>
            <div class="col-sm-6">Heading</div>
            <div class="col-sm-8">Default (Normal description)</div>
            <div class="col-sm-2">Sort Order</div>
            <div class="col-sm-4">Type</div>
            <div class="col-sm-2">Action</div>
        </div>        
        {% for form in formset %}
            <div class="col-sm-6">
                {{ form.name }}
            </div>
            <div class="col-sm-8">
                <div class="input-group">
                    {{ form.default }}
                </div>
            </div>
            <div class="col-sm-2">
                <div class="input-group">
                    {{ form.sortorder }}
                </div>
            </div>
            <div class="col-sm-4">
                <div class="input-group">
                    {{ form.fieldtype }}
                </div>
            </div>
            <div class="col-sm-2">
                <div class="input-group">                    
                    <div class="input-group-append">
                        <button id="add{{ forloop.counter0 }}" class="btn btn-success add-row">+</button>
                    </div>
                    <div class="input-group-append">
                        <button id="del{{ forloop.counter0 }}" class="btn btn-danger del-row">-</button>
                    </div>
                </div>
            </div>
        </div>
        {% endfor %}        
    {% endif %}
    <div class="row my-3">
        <div class="col-sm-8"></div>
        <div class="col-sm-8">
            <div class="input-group">                    
                <div class="input-group-append mx-1">
                    <button id="save_report" type="submit" class="btn btn-success"><i class="fal fa-shield-check"></i> Save Report Format</button>
                </div>
                <div class="input-group-append mx-1">
                    <button id="save_report" type="button" class="btn btn-danger"><i class="fal fa-times-hexagon"></i> Cancel</button>
                </div>
            </div>    
        </div>
        <div class="col-sm-8"></div>
    </div>
    </form>
</div>
{% endblock %}

完整跟踪:

[02/Feb/2019 22:59:16] "POST /clinic/joelent/procedures/template/modify/3 HTTP/1.1" 500 99735
[02/Feb/2019 22:59:17] "GET /favicon.ico/ HTTP/1.1" 200 14
<bound method MultiValueDict.get of <QueryDict: {'csrfmiddlewaretoken': ['7G5XVYRwt3LsrL9oXr9yOeaIU6HLYwu9cJtE8UpVB3R67Lb7oU8QXQ5kzfBJDJSP'], 'form-0-name': ['External ear canal'], 'form-0-default': ['Bilateral external ear canals appear normal. No discharge.'], 'form-0-sortorder': ['100'], 'form-0-fieldtype': ['heading1'], 'form-1-name': ['Tympanic membrane'], 'form-1-default': ['Tympanic membrane appears normal. Mobility not assessed.'], 'form-1-sortorder': ['500'], 'form-1-fieldtype': ['heading1'], 'form-2-name': [''], 'form-2-default': [''], 'form-2-sortorder': ['1000'], 'form-2-fieldtype': ['heading1']}>>
2019-02-02 23:04:03,415 django.request ERROR    Internal Server Error: /clinic/joelent/procedures/template/modify/3
Traceback (most recent call last):
File "/home/joel/.local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
File "/home/joel/.local/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
    response = self.process_exception_by_middleware(e, request)
File "/home/joel/.local/lib/python3.6/site-packages/django/core/handlers/base.py", line 124, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/joel/myappointments/clinic/views.py", line 5664, in procedure_template_modification_alt
    if formset.is_valid():
File "/home/joel/.local/lib/python3.6/site-packages/django/forms/formsets.py", line 301, in is_valid
    self.errors
File "/home/joel/.local/lib/python3.6/site-packages/django/forms/formsets.py", line 281, in errors
    self.full_clean()
File "/home/joel/.local/lib/python3.6/site-packages/django/forms/formsets.py", line 322, in full_clean
    for i in range(0, self.total_form_count()):
File "/home/joel/.local/lib/python3.6/site-packages/django/forms/formsets.py", line 110, in total_form_count
    return min(self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max)
File "/home/joel/.local/lib/python3.6/site-packages/django/utils/functional.py", line 37, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
File "/home/joel/.local/lib/python3.6/site-packages/django/forms/formsets.py", line 92, in management_form
    code='missing_management_form',
django.core.exceptions.ValidationError: ['ManagementForm data is missing or has been tampered with']

1 个答案:

答案 0 :(得分:1)

表单集的问题在于您不知道表单集将包含多少个表单。表单集通常附带一些Javascript,以允许添加新表单或删除旧表单。需要管理表格来跟踪这些更改。

如果您未包含管​​理表单或该表单与POST数据不匹配,则将引发您遇到的异常。要解决此问题,只需提供管理表格即可。


  

来自django documentation

     
    

管理表单可用作表单集的属性     本身。在模板中呈现表单集时,可以包括所有     通过呈现 {{my_formset.management_form}} 来管理数据     (替换为您的表单集的名称)。

  

因此,在您的模板中只需添加管理表单即可:

<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}        
    {{ formset.management_form }}
    {# Your formset rendering... #}
</form>