Comment déboguer un Django MultiValueDictKeyError sur Formset POST

quand je poste mon formset, j'obtiens une MultiValueDictKeyError. Plus précisément:

MultiValueDictKeyError at /core/customers/1/update/documents/
"Key u'documents-0-attachment_ptr' not found in <QueryDict: {u'documents-1-last_modified_date': [u''], u'documents-1-name': [u''], u'documents-MAX_NUM_FORMS': [u''], u'documents-0-attachment_file': [u''], u'documents-INITIAL_FORMS': [u'1'], u'documents-1-document_type': [u''], u'documents-0-notes': [u''], u'documents-1-notes': [u''], u'submit': [u'Submit changes'], u'documents-0-DELETE': [u'on'], u'documents-1-attachment_file': [u''], u'documents-0-document_type': [u''], u'documents-TOTAL_FORMS': [u'2'], u'documents-0-name': [u'test'], u'documents-1-creation_date': [u''], u'documents-0-creation_date': [u'2012-12-01 23:41:48'], u'csrfmiddlewaretoken': [u'NCQ15jA7erX5dAbx20Scr3gWxgaTn3Iq', u'NCQ15jA7erX5dAbx20Scr3gWxgaTn3Iq', u'NCQ15jA7erX5dAbx20Scr3gWxgaTn3Iq'], u'documents-0-last_modified_date': [u'2012-12-01 23:41:48']}>"

la clé, C'est que Django cherche la clé documents-0-attachment_ptr dans les données post. Cela prête à confusion -- un Document est une sous-classe d'une pièce jointe. Toutes les autres données post est comme prévu. Pourquoi Django a-t-il besoin de données pointer dans mon formset?

Voici la forme utilisée dans le formset:

class DocumentInlineForm(forms.ModelForm):  # pylint: disable=R0924
    attachment_file = forms.FileField(widget=NoDirectoryClearableFileInput)
    notes = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={'rows': 2,}), 
    )
    helper = DocumentInlineFormHelper()

    class Meta: # pylint: disable=W0232,R0903
        fields = (
            'attachment_file', 
            'creation_date',
            'document_type',
            'last_modified_date',
            'name',
            'notes',
        )
        model = Document

Et voici le Document modèle:

"""
Handles document model definitions.
"""
from django.db import models
from eee_core.models.attachments import Attachment
from django.db.models.signals import pre_save
from datetime import datetime
from django.utils.timezone import utc

class Document(Attachment):
    """
    A document is an attachment with additional meta data.
    """
    creation_date = models.DateTimeField(
        blank=True, 
        null=True,
    )
    document_type = models.CharField(
        blank=True, 
        choices=(
            ('CONTRACT', 'Contract'),
            ('INVOICE', 'Invoice'),
            ('FACILITY', 'Facility change form'),
            ('LOA', 'Letter of authorization'),
            ('USAGE', 'Usage history document'),
            ('OTHER', 'Other'),
        ),
        default=None, 
        null=True, 
        max_length=8, 
    )
    last_modified_date = models.DateTimeField(
        blank=True, 
        null=True,
    )
    notes = models.TextField(
        blank=True,
        null=True,
    )

    class Meta(Attachment.Meta): # pylint: disable=W0232,R0903
        """
        Sets meta fields for model.
        """
        app_label = 'core'

    def __str__(self):
        return unicode(self).encode('utf-8')

    def __unicode__(self):
        return unicode(self.name)

def pre_save_callback(sender, instance, *args, **kwargs): # pylint: disable=W0613
    if not isinstance(instance, Document):
        return

    if not instance.creation_date:
        instance.creation_date = datetime.utcnow().replace(tzinfo=utc)

    instance.last_modified_date = datetime.utcnow().replace(tzinfo=utc)

pre_save.connect(pre_save_callback, dispatch_uid='document_pre_save')

informations Supplémentaires:

curieusement, le poteau d'entrée du formset fonctionne très bien. Ce n'est que sur les messages de mise à jour -- quand il y a des formulaires initiaux dans le formset -- quand je reçois cette erreur. Cela arrive aussi quand j'essaie de supprimer des formulaires du formset.

en outre, le formset est un formset Générique En Ligne utilisant des formes croustillantes django.

mise à Jour

il y a eu une demande pour le code du modèle utiliser. Voici la version simplifiée:

{% load crispy_forms_tags %}
{% load url from future %}
<form action="" method="post" enctype="multipart/form-data">
    {{ formset.management_form }}
    {% for subform in formset.forms %}
      {{ subform.id }}
      {% crispy subform %}
    {% endfor %}
    <div class="btn-toolbar">
        <input class='btn btn-primary' type="submit" name="submit" value="Submit changes" />
    </div>
</form>
15
demandé sur Erik 2012-12-02 10:02:48

4 réponses

ce n'est pas le cas avec OP, mais vous rencontrerez un MultiValueDictKeyError, si certains des champs cachés sont manquants dans le modèle. Cela peut arriver quand au lieu de rapide et sale {{form}}, les champs sont répertoriés dans le modèle, un par un: {{form.field1}},{{form.field2}}, tout en laissant des champs cachés.

Pour inclure les ramener de faire quelque chose le long des lignes (pour chaque formulaire dans formset):

{% for hidden in form.hidden_fields %}
    {{ hidden }}
{% endfor %}

ou

{% for form in formset %}    
    {% for hidden in form.hidden_fields %}
        {{ hidden }}
    {% endfor %}    
{% endfor %}
48
répondu mp31415 2015-04-23 20:27:35

j'utilise aussi

{% for hidden in form.hidden_fields %}
    {{ hidden }}
{% endfor %}

comme ceci

<div role="tabpanel" class="tab-pane active" id="email">
                {% csrf_token %}
                {{ eformset.management_form}}
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <div id="addemail" class="btn btn-success">
                                <span class="glyphicon glyphicon-plus" > 
                                </span>
                            </div>
                            <p><br></p>
                            {% for f in eformset %}
                                {% for hidden in f.hidden_fields %}
                                    {{ hidden }}
                                {% endfor %}
                                <div class="item_email_set">
                                    <table class="table table-condensed table-bordered">
                                    <tr>
                                        {% for field in f.visible_fields %} <!---->
                                            <td>
                                                {{ field.label }}
                                            </td>
                                        {% endfor %}

                                        <td>
                                        </td>
                                    </tr>
                                    <tr>
                                        {% for field in f.visible_fields %}
                                            <td>
                                                {{field.errors.as_ul}}
                                                {{field}}
                                            </td>
                                        {% endfor %}    
                                        <td class="btncolumn">      
                                            <p style="">
                                                <a class="delete_email_set" href="#">
                                                    <div  class="btn btn-danger">
                                                        <span class="glyphicon glyphicon-remove" > 
                                                        </span>
                                                    </div>
                                                </a>
                                            </p>
                                        </td>
                                    </tr>   

                                </table>
                            </div>  
                        {% endfor %}
                    </div>
                </div>  
            </div>

et je résoudre le MultiValueDictKeyError

4
répondu Omar Giorgetti 2015-08-07 21:33:21

j'ai arrêté cette erreur en ajoutant attachment_ptr pour la liste des champs de mon formulaire. Donc DocumentInlineForm est:

class DocumentInlineForm(forms.ModelForm):  # pylint: disable=R0924
    attachment_file = forms.FileField(widget=NoDirectoryClearableFileInput)
    notes = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={'rows': 2,}), 
    )
    helper = DocumentInlineFormHelper()

    class Meta: # pylint: disable=W0232,R0903
        fields = (
            'attachment_ptr',
            'attachment_file', 
            'creation_date',
            'document_type',
            'last_modified_date',
            'name',
            'notes',
        )
        model = Document

peut-être que c'est quelque chose que je ne savais pas avant, mais est-ce que Django exige que vous fournissiez un pointeur vers la superclasse dans toutes les formes qui utilisent un modèle subclassé? Ce qui me surprend.

j'aimerais savoir pourquoi ce champ pointeur est nécessaire, donc j'ai ouvert une question pour y répondre ici: Pourquoi mon formset django a besoin d'un champ de pointeur de référence?.

1
répondu Erik 2017-05-23 12:10:36

ce n'est pas la réponse à L'OP mais j'ai eu la même erreur. J'ai enlevé par erreur {{ subform.id }} de mon modèle car je ne pouvais pas le voir visuellement et je rangeais le vieux code. Dans votre HTML vous obtiendrez quelque chose comme:

<input name="note_set-0-id" value="34632" id="id_note_set-0-id" type="hidden">
0
répondu hum3 2018-03-03 10:00:16