Django - can't get modelchoice FK while submitting form

121 Views Asked by At

I have a custom form form signup, referencing a "categorie" model on a modelChoiceField. As I try to submit it, it fails on getting the reference to the model and raises the following error :

IntegrityError at /marketplace/signsupplier/ null value in column "categorie_id" violates not-null constraint DETAIL: Failing row contains (55, , , , , , null).

It's as if it loses all posted datas, but I can see in the debug that categorie_id is well passed in the POST datas.

Here is my model.py :

class Supplier(models.Model):
        phone_regex     = RegexValidator(regex=r'^(87|89)\d{6}$', message="Entrez un numéro de portable Polynésien valide")
        user            = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
        enseigne        = models.CharField(max_length=128, blank=False, null=False)
        vini            = models.CharField(verbose_name="Vini",validators=[phone_regex], max_length=8, blank=False, null=False)
        numero_tahiti   = models.CharField(max_length=9, blank=False, null=False)
        immat_rcs       = models.CharField(max_length=9, blank=False, null=False)
        categorie       = models.ForeignKey(Categorie, on_delete=models.DO_NOTHING)
        labels          = models.CharField(max_length=128)

my forms.py :

class SignupSupplierForm(UserCreationForm):
        email           = forms.EmailField(required=True)
        first_name      = forms.CharField(required=True,label="Prenom")
        last_name       = forms.CharField(required=True,label="Nom")
        vini            = forms.CharField(required=True, max_length=8,validators=[phone_regex])
        enseigne        = forms.CharField(required=True, max_length=128, label="Enseigne")
        numero_tahiti   = forms.CharField(required=True, max_length=9, label="Numéro Tahiti")
        immat_rcs       = forms.CharField(required=True, max_length=9, label="Immatriculation RCS")
        categorie       = forms.ModelChoiceField(required=False,queryset=Categorie.objects.all())
        labels          = forms.CharField(required=False,max_length=128, label="Labels (option)")

        class Meta(UserCreationForm.Meta):
                model = User
                fields = ('email','first_name','last_name')

        @transaction.atomic
        def save(self):
                user = super().save(commit=False)
                user.is_supplier = True
                user.save()
                supplier = Supplier.objects.create(user=user)
                supplier.vini = self.cleaned_data.get('vini')
                supplier.enseigne = self.cleaned_data.get('enseigne')
                supplier.numero_tahiti = self.cleaned_data.get('numero_tahiti')
                supplier.immat_rcs = self.cleaned_data.get('immat_rcs')
                supplier.categorie = self.cleaned_data['categorie'].id
                supplier.labels = self.cleaned_data.get('labels')
                supplier.save()
                return user

And views.py

class SignupSupplierView(CreateView):
        model           = User
        form_class      = SignupSupplierForm
        success_url     = reverse_lazy('home')
        template_name   = 'signup.html'

        def get_context_data(self, **kwargs):
                return super().get_context_data(**kwargs)

        def form_valid(self, form):
                user = form.save()
                login(self.request, user)
                return HttpResponseRedirect(self.success_url)

I know it's a problem with the "categorie" field, but can't figure out how to make ot work.

As asked, here is the content of the POST data, from the debug panel :

Variable             Value
csrfmiddlewaretoken  'TGTAW...qn17h'
email                '[email protected]'
first_name           'pre four'
last_name            'nom four'
password1            'test456'
password2            'test456'
vini                 '87740921'
enseigne             'ens four'
numero_tahiti        '123456789'
immat_rcs            '123456789'
categorie            '2'
labels      'test'

I also used logging features to get more informations on the error, and I got this. Maybe it could help :

(0.001) DECLARE "_django_curs_139721501058880_sync_1" NO SCROLL CURSOR WITH HOLD FOR SELECT "ELIEN_CORE_categorie"."id", "ELIEN_CORE_categorie"."categorie", "ELIEN_CORE_categorie"."timestamp", "ELIEN_CORE_categorie"."updated" FROM "ELIEN_CORE_categorie"; args=()
Exception while resolving variable 'object_list' in template 'signup.html'.

and then, a little bit further :

Traceback (most recent call last):
  File "/home/max/elien_dev/elien/lib/python3.8/site-packages/django/template/base.py", line 848, in _resolve_lookup
    raise VariableDoesNotExist("Failed lookup for key "
django.template.base.VariableDoesNotExist: Failed lookup for key [mapbox_key] in [{'True': True, 'False': False, 'None': None}, {'csrf_token': <SimpleLazyObject: '96lE9Cepu2rqn5MbTGqtqmLdsz0LY1dGodSUIsOlqhU1bMj78EZzmLryqI8o7bkU'>, 'request': <WSGIRequest: GET '/marketplace/signsupplier/'>, 'user': <SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x7f1cee9b9850>>, 'perms': <django.contrib.auth.context_processors.PermWrapper object at 0x7f1cee92d580>, 'messages': <django.contrib.messages.storage.fallback.FallbackStorage object at 0x7f1ceea002e0>, 'DEFAULT_MESSAGE_LEVELS': {'DEBUG': 10, 'INFO': 20, 'SUCCESS': 25, 'WARNING': 30, 'ERROR': 40}}, {}, {'form': <SignupSupplierForm bound=False, valid=False, fields=(email;first_name;last_name;password1;password2;vini;enseigne;numero_tahiti;immat_rcs;categorie;labels)>, 'view': <ELIEN_MARKETPLACE.views.SignupSupplierView object at 0x7f1ceea00370>}, {}]
(0.001) SELECT "ELIEN_CORE_categorie"."id", "ELIEN_CORE_categorie"."categorie", "ELIEN_CORE_categorie"."timestamp", "ELIEN_CORE_categorie"."updated" FROM "ELIEN_CORE_categorie" WHERE "ELIEN_CORE_categorie"."id" = 3 LIMIT 21; args=(3,)
(0.001) SELECT (1) AS "a" FROM "ELIEN_CORE_user" WHERE "ELIEN_CORE_user"."email" = '[email protected]' LIMIT 1; args=('[email protected]',)
(0.001) INSERT INTO "ELIEN_CORE_user" ("password", "last_login", "is_superuser", "first_name", "last_name", "is_staff", "is_active", "date_joined", "email", "email_confirmed", "is_client", "is_supplier") VALUES ('p...w=', NULL, false, 'pre four', 'nom four', false, true, '2020-12-08T07:04:56.897584+00:00'::timestamptz, '[email protected]', false, false, true) RETURNING "ELIEN_CORE_user"."id"; args=('p...=', None, False, 'pre four', 'nom four', False, True, datetime.datetime(2020, 12, 8, 7, 4, 56, 897584, tzinfo=<UTC>), '[email protected]', False, False, True)
(0.001) INSERT INTO "ELIEN_MARKETPLACE_supplier" ("user_id", "enseigne", "vini", "numero_tahiti", "immat_rcs", "categorie_id", "labels") VALUES (63, '', '', '', '', NULL, ''); args=(63, '', '', '', '', None, '')
(0.000) SELECT "ELIEN_MARKETPLACE_supplier"."user_id", "ELIEN_MARKETPLACE_supplier"."enseigne", "ELIEN_MARKETPLACE_supplier"."vini", "ELIEN_MARKETPLACE_supplier"."numero_tahiti", "ELIEN_MARKETPLACE_supplier"."immat_rcs", "ELIEN_MARKETPLACE_supplier"."categorie_id", "ELIEN_MARKETPLACE_supplier"."labels" FROM "ELIEN_MARKETPLACE_supplier" LIMIT 21; args=()
(0.000) SELECT "ELIEN_MARKETPLACE_supplier"."user_id", "ELIEN_MARKETPLACE_supplier"."enseigne", "ELIEN_MARKETPLACE_supplier"."vini", "ELIEN_MARKETPLACE_supplier"."numero_tahiti", "ELIEN_MARKETPLACE_supplier"."immat_rcs", "ELIEN_MARKETPLACE_supplier"."categorie_id", "ELIEN_MARKETPLACE_supplier"."labels" FROM "ELIEN_MARKETPLACE_supplier" LIMIT 21; args=()
Internal Server Error: /marketplace/signsupplier/
Traceback (most recent call last):
  File "/home/max/elien_dev/elien/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.NotNullViolation: null value in column "categorie_id" violates not-null constraint
DETAIL:  Failing row contains (63, , , , , , null).

Thanks for your help !

1

There are 1 best solutions below

6
On

Here is the issue,

supplier.categorie = self.cleaned_data['categorie'].id

self.cleaned_data['categorie'] itself contains the id of categorie instance, so you don't need to do

supplier.categorie = self.cleaned_data['categorie'].id

just

 supplier.categorie = self.cleaned_data['categorie']