pythondjangocreate-view

Error Assigning Foreign Key in Django CreateView, can't get an instance


Issue: Cannot Assign "38" to "Pusinex2.seccion" - Must Be a "Seccion" Instance

Models:

Views:

Error:

Here's my models:

class Seccion(models.Model):
    distrito = models.ForeignKey(Distrito, on_delete=models.CASCADE)
    municipio = models.ForeignKey(Municipio, on_delete=models.CASCADE)
    seccion = models.PositiveSmallIntegerField(primary_key=True)
    tipo = models.PositiveSmallIntegerField(choices=CAT_TIPO)
    activa = models.BooleanField(default=True)


class Pusinex2(models.Model):
    seccion = models.ForeignKey(Seccion, on_delete=models.CASCADE)
    f_act = models.DateField()
    hojas = models.PositiveSmallIntegerField()
    observaciones = models.TextField(blank=True, null=True)
    archivo = models.FileField(upload_to=pusinex_file, blank=True, null=True)
    # Trazabilidad
    user = models.ForeignKey(User, editable=False, on_delete=models.CASCADE)

a FormView:

class PUSINEXForm(forms.ModelForm):
    seccion = forms.IntegerField()
    f_act = forms.DateField()
    hojas = forms.IntegerField()
    archivo = forms.FileField()
    observaciones = forms.CharField(widget=forms.Textarea, required=False)

And a related CreateView:

class CreatePUSINEX(LoginRequiredMixin, CreateView):
    template_name = 'control/pusinex_form.html'
    form_class = PUSINEXForm
    model = Pusinex2
    login_url = reverse_lazy('login')
    redirect_field_name = 'next'

    def form_invalid(self, form):
        logger.error(form.errors)
        return super(CreatePUSINEX, self).form_invalid(form)

    def form_valid(self, form):
        seccion = Seccion.objects.get(seccion=form.cleaned_data['seccion'])
        pusinex = form.save(commit=False)
        pusinex.seccion = seccion
        pusinex.user = self.request.user
        pusinex.save()
        return super(CreatePUSINEX, self).form_valid(form)

    def get_success_url(self):
        return reverse('municipio', kwargs={'pk': self.object.seccion.municipio.id})

When trying to save the form, I get this error:

ValueError at /creation/

Cannot assign "38": "Pusinex2.seccion" must be a "Seccion" instance.

However, I'm supposed to do that on this line on CreatePusinex.form_valid(): seccion=form.cleaned_data['seccion']) pusinex = form.save(commit=False). How can I fix this error?


Solution

  • This is a common issue when assigning FKs inside Forms. You are passing the PK of the Seccion instance as an integer field in your Pusinex form as below:

    class PUSINEXForm(forms.ModelForm):
        seccion = forms.IntegerField()
        ##  Other fields the same
    

    But you need it to be an actual instance of the Seccion model instead, as below:

    class PUSINEXForm(forms.ModelForm):
        seccion = forms.ModelChoiceField(queryset=Seccion.objects.all())
        ##  Other fields the same
        ##  Note: make sure you actually want the queryset to be 'all',
        ##        you may find you want to filter/exclude some instances
    

    Next you need to change the form_valid method inside your CreateView, because Django now takes care of fetching the queryset for you (which is simpler, and more 'paradigmatically Django'):

    ##  Inside class CreatePUSINEX(LoginRequiredMixin, CreateView):
    def form_valid(self, form):
        pusinex = form.save(commit=False)
        pusinex.user = self.request.user
        pusinex.save()
        return super(CreatePUSINEX, self).form_valid(form)
    

    Once you make the above changes your Create View and Form should work now correctly.


    For what it's worth, the way you are defining your ModelForm is also quite odd: typically you would define it as below:

    class PUSINEXForm(forms.ModelForm):
        class Meta:
            model = Pusinex2
            fields = ['seccion', 'f_act', 'hojas', 'archivo', 'observaciones']
            widgets = {
                'observaciones': forms.Textarea(attrs={'cols': 40, 'rows': 10}),
                ##  Other custom field widgets can be defined here as needed.
            }
    
        def __init__(self, *args, **kwargs):
            super(PUSINEXForm, self).__init__(*args, **kwargs)
            self.fields['seccion'].queryset = Seccion.objects.all()