3
votes

i'm learning django and Python. I have a problem with a form.

the error is "TypeError at /My_app" and "cannot unpack non-iterable int object"

this is my views :

from django.http import HttpResponse, Http404
from django.shortcuts import redirect, render, get_object_or_404
from datetime import datetime

from Qualite.forms import NCForm
from Qualite.models import NC, Nc_type, Poste

def view_accueil(request):

    form = NCForm(request.POST or None)

    if form.is_valid():
        newNc = NC()
        newNc.idaffaire = form.cleaned_data['idaffaire']
        newNc.idnc = form.cleaned_data['idnc']
        newNc.idof = form.cleaned_data['idof']
        newNc.idposte = form.cleaned_data['idposte']
        newNc.idrepere = form.cleaned_data['idrepere']
        newNc.iquantite = form.cleaned_data['iquantite']
        newNc.save()

    return render(request, 'Qualite/accueil.html', locals())

my forms :

from django import forms
from .models import Nc_type, NC, Poste

class NCForm(forms.Form):

    #choices = NC.objects.values_list('id', 'idaffaire')
    ncs = NC.objects.values_list('idaffaire', flat = True)

    idaffaire = forms.ChoiceField(choices = (ncs))
    idof = forms.CharField()
    idrepere = forms.CharField()
    idposte = forms.CharField()
    idnc = forms.CharField()
    quantite = forms.CharField()

and my model

from django.db import models
from django.utils import timezone

class Nc_type(models.Model):
    nom = models.CharField(max_length=30)

    def __str__(self):
        return self.nom

class Poste(models.Model):
    nom = models.CharField(max_length=50)

    def __str__(self):
        return self.nom


class NC(models.Model):
    idaffaire = models.CharField(max_length=4, db_column='idAffaire')
    idof = models.IntegerField(db_column='idOf')
    idposte = models.ForeignKey('Poste', models.DO_NOTHING, db_column="idPoste", default=1)
    idrepere = models.IntegerField(db_column='idRepere')
    idnc = models.ForeignKey(Nc_type, models.DO_NOTHING, db_column='idNc_type', default=1)
    quantite = models.PositiveIntegerField()
    dateajout = models.DateTimeField(default=timezone.now, db_column='dateAjout')

and the template:

<h1>Ajout d'une NC</h1>

<form action="{% url "accueil" %}" method="GET">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit" />
</form>

is it someone to help me to understand the problem. I searched solution on the web but no way.

1
Why don't you use a ModelForm such that most of the boilerplate code can be removed.Willem Van Onsem

1 Answers

2
votes

The problem is that a query like:

ncs = NC.objects.values_list('idaffaire', flat=True)

will result in ncs being an interable of int objects. But the choices of a ChoiceField require a list of 2-tuples with the key as first item of these 2-tuples, and the label as second item of the 2-tuples.

Nevertheless, using a query at the class level is not a good idea at all. It means that the query will be performed with the class is loaded. This means that if you later add an extra NC object, the form will not offer this as a new choice.

I would also advise to make use of a ModelForm [Django-doc] instead, since that will remove a lot of boilerplate code, especially since idposte for example needs to use a valid value for the Poste object, and here your form can not validate that.

You can then implement the form as:

from django import forms
from .models import NC

class NCForm(forms.ModelForm):
    class Meta:
        model = NC
        fields = '__all__'

Then in your views.py, you can use this form with:

from django.shortcuts import redirect

def view_accueil(request):
    if request.method == 'POST':
        form = NCForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('name-of-a-view')
    else:
        form = NCForm()
    return render(request, 'Qualite/accueil.html', {'form': form})

Note: In case of a successful POST request, you should make a redirect [Django-doc] to implement the Post/Redirect/Get pattern [wiki]. This avoids that you make the same POST request when the user refreshes the browser.