Issue: Cannot Assign "38" to "Pusinex2.seccion" - Must Be a "Seccion" Instance
Models:
Seccion
and Pusinex2
are related in a ForeignKey manner.Seccion
holds the primary key field and other details related to a section.Pusinex2
has a foreign key relationship with Seccion
along with fields for date, file, and user traceability.Views:
PUSINEXForm
is defined in the forms.py, linking various form fields to the respective model fields.CreatePUSINEX
view handles the form submission, using PUSINEXForm
and Pusinex2
.Error:
ValueError at /creation/
.CreatePUSINEX
view's form_valid
method.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?
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()