Правильное использование modelformset_factory для использования formset - PullRequest
0 голосов
/ 02 февраля 2019

Описание проблемы: Я создаю набор форм для отображения всех данных в одной из моих моделей и их редактирования.Мое представление отображается правильно, и отображаются правильные данные, сохраненные в модели.Но на 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 голосов
/ 02 февраля 2019

Проблема с наборами форм заключается в том, что вы не знаете, сколько форм будет содержать ваш набор форм.Наборы форм обычно сопровождаются некоторым Javascript, позволяющим добавлять новые формы или удалять старые.Форма управления необходима для отслеживания этих изменений.

Если вы не включите форму управления или она не совпадает с данными POST при условии, что возникнет исключение, с которым вы столкнулись.Чтобы это исправить, просто включите форму управления.


Из документации django :

Форма управления доступна как атрибут самого набора форм.При отображении набора форм в шаблоне вы можете включить все данные управления, отобразив {{my_formset.management_form}} (при необходимости подставляя имя вашего набора форм).

Так что в свой шаблон просто добавьте форму управления:

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