I have a problem trying to validate a modelform and, after various tests, I think the problem is with the html template in how the information is displayed.
I have 2 models:
models.py:
class bdAccesorios(models.Model):
fdClienteAcc=models.CharField(max_length=35)
fdProveedorAcc=models.CharField(max_length=60)
fdSkuAcc=models.CharField(max_length=30)
fdNombreAcc=models.CharField(max_length=60)
fdCostoAcc=models.DecimalField(max_digits=8, decimal_places=2)
fdUnidadAcc=models.CharField(max_length=30)
fdExistenciaAcc=models.IntegerField()
fdAuxAcc=models.CharField(max_length=60, default="0")
class bdComponentes(models.Model):
fdGrupoComp=models.CharField(max_length=30)
fdNombreComp=models.CharField(max_length=60)
fdSkuComp=models.CharField(max_length=30)
fdExistenciaComp=models.DecimalField(max_digits=8, decimal_places=2)
fdDesgloseComp=models.ManyToManyField(bdAccesorios, through="bdComponentesDesglose")
fdPertenenciaComp=models.ForeignKey(bdUsuariosAux , on_delete=models.CASCADE)
fdAuxComp=models.CharField(max_length=60, default="0")
and establish a many to many relationship through a third model to
class bdComponentesDesglose(models.Model):
fdAccesorioCompDes=models.ForeignKey(bdAccesorios, on_delete=models.CASCADE)
fdComponenteCompDes=models.ForeignKey(bdComponentes, on_delete=models.CASCADE)
fdCantidadCompDes=models.IntegerField(default=1)
fdPrecioTotalAcc=models.DecimalField(max_digits=8, decimal_places=2, blank="true",
editable="false")
To update bdComponentes I combine two forms
forms.py:
class fmComponente(ModelForm):
class Meta:
model=bdComponentes
fields='__all__'
exclude = ('fdAuxComp', 'fdDesgloseComp')
labels = {
'fdGrupoComp': 'Grupo',
'fdSkuComp': 'SKU',
'fdNombreComp': 'Nombre',
'fdPertenenciaComp': 'Cliente',
'fdExistenciaComp': 'Existencia',
}
class fmComponenteDes(ModelForm):
class Meta:
model=bdComponentesDesglose
fields='__all__'
exclude = ('fdComponenteCompDes','fdPrecioTotalAcc',)
labels = {
'fdAccesorioCompDes': 'Accesorio',
'fdCantidadCompDes': 'Cantidad',
}
Then I make a view to update the model
views.py:
def vwComponenteModificar(request,IDComp):
vrModificarComp = bdComponentes.objects.get(id=IDComp)
vrModificarCompDes = bdComponentesDesglose.objects.filter(fdComponenteCompDes__fdNombreComp=vrModificarComp.fdNombreComp)
vrComponenteForm=fmComponente(instance=vrModificarComp)
vrComponenteDesModelForm = modelformset_factory(bdComponentesDesglose, extra=0, exclude = ('fdComponenteCompDes','fdPrecioTotalAcc',))
vrComponenteDesForm=vrComponenteDesModelForm(queryset=vrModificarCompDes)
if request.method == "POST":
vrComponenteForm = fmComponente(request.POST, instance=vrModificarComp)
vrComponenteDesForm=vrComponenteDesModelForm(request.POST)
if vrComponenteForm.is_valid() and vrComponenteDesForm.is_valid(): ###Here is the problem
vrComponenteForm.save()
for lpCompDes in vrComponenteDesForm:
lpCompDes.save()
return redirect('/')
return render(request,"Myapp/ComponentesCrear.html",{
'dtCrearComp': vrComponenteForm,
'dtCrearCompDes': vrComponenteDesForm
})
My problem is in my template the view, previously mention, works perfectly when my template is like this
template:
<form action="" method="post">
{% csrf_token %}
<table>
{{ dtCrearComp.management_form}}
{{dtCrearComp}}
<div class="">
{{dtCrearCompDes.management_form}}
{{dtCrearCompDes}}
</div>
</table>
<input type="submit" name="Terminar" value="Terminar">
</form>
But i need to change my template to this, because I added some javascript logic to add buttons that add formset fields dynamically
<form class="" method="POST" action="">
{% csrf_token %}
<div class="">
{{dtCrearComp.management_form}}
<table>
{{dtCrearComp}}
<table>
</div>
{{dtCrearCompDes.management_form}}
{% for lpCrearCompDes in dtCrearCompDes %}
<div class="row form-row spacer">
<label>{{lpCrearCompDes.fdAccesorioCompDes.label}}</label>
{{lpCrearCompDes.fdAccesorioCompDes}}
<label>{{lpCrearCompDes.fdCantidadCompDes.label}}</label>
{{lpCrearCompDes.fdCantidadCompDes}}
<button class="btn btn-success add-form-row">+</button>
</div>
{% endfor %}
And this new template gives me in my view a vrComponenteDesForm.is_valid()=False
After some trial and error, I found the solution by changing the template to this:
{{dtCrearCompDes.management_form}}
{% for lpCrearCompDes in dtCrearCompDes %}
<div class="row form-row spacer">
{{lpCrearCompDes.as_p}}
<button class="btn btn-success add-form-row">+</button>
</div>
{% endfor %}