简体   繁体   English

正确使用modelformset_factory以使用formset

[英]Properly using modelformset_factory to use formset

Problem Description: I am building a formset to display all data in one of my models, and to edit it. 问题描述:我正在构建一个表单集,以显示一个模型中的所有数据并进行编辑。 My view is rendered properly, and the correct data as saved in the model is displayed. 我的视图已正确渲染,并显示了保存在模型中的正确数据。 But on POST, I get an exception, stating that "ManagementForm data is missing or has been tampered with". 但是在POST上,我遇到一个异常,指出“ ManagementForm数据丢失或已被篡改”。 I would like to know why this happens. 我想知道为什么会这样。 Complete code and trace is given below. 完整的代码和跟踪在下面给出。

My model: 我的模特:

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}]'

My Form: 我的表格:

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()}
    )

My view: 我的观点:

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(),
    })

My template: 我的模板:

{% 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 %}

Complete trace: 完整的跟踪:

[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']

The issue with formsets is that you don't know how many forms your formset will contain. 表单集的问题在于您不知道表单集将包含多少个表单。 Formsets are usually accompanied by some Javascript to allow adding new forms or deleting old ones. 表单集通常附带一些Javascript,以允许添加新表单或删除旧表单。 The management form is required to keep track of these changes. 需要管理表单来跟踪这些更改。

If you fail to include the management form or it doesn't match with the POST data provided the Exception you encountered will be raised. 如果您未包含管​​理表单,或者该表单与POST数据不匹配,则将引发您遇到的异常。 To fix it, simply include the management form. 要解决此问题,只需提供管理表格即可。


From the django documentation : Django文档中

The management form is available as an attribute of the formset itself. 管理表单可用作表单集本身的属性。 When rendering a formset in a template, you can include all the management data by rendering {{ my_formset.management_form }} (substituting the name of your formset as appropriate). 在模板中呈现表单集时 ,可以通过呈现{{my_formset.management_form}} (适当地替换表单集的名称)来包含所有管理数据。

So in your template just add the management form: 因此,只需在模板中添加管理表单即可:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用modelformset_factory和占位符 - using modelformset_factory and placeholder 使用modelformset_factory和访问对象的属性 - Using modelformset_factory and access attributes of an object 使用Django内联formset获取&#39;modelformset_factory而不定义&#39;字段&#39;错误&#39;。 我究竟做错了什么? - Getting a 'modelformset_factory without defining 'fields' error' using Django inline formset. What am I doing wrong? 使用modelformset_factory时,如何在Django中区分表单集? - How to differ formsets in django when using modelformset_factory? Django:使用 modelformset_factory 填充多对多字段 - Django: populating many to many field using modelformset_factory Django modelformset_factory UnboundLocalError - Django modelformset_factory UnboundLocalError 多 FormSet 错误“禁止在未明确定义‘字段’或‘排除’的情况下调用 modelformset_factory” - Multi FormSet error “Calling modelformset_factory without defining 'fields' or 'exclude' explicitly is prohibited” 使用modelformset_factory将数据添加到Django表单类 - Add data to Django form class using modelformset_factory modelformset_factory和csrf令牌丢失或不正确 - modelformset_factory and csrf token missing or incorrect 在 Django 中验证 modelformset_factory 时出现问题 - Problem validating modelformset_factory in Django
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM