Two months ago I started using Django. Right now I have the CBV of a form with two elements, a Parent Model and a Child Model that can be multiple. I have functional part of Create, Detail, List and Delete. I have the problem when editing with the form of the Father model with multiple Formsets of the Son Model, because when it saves the new information, it creates new elements instead of updating them (For example, if I edit 3 instances of the Son , creates 3 objects with the new information and keeps the 3 previous children unchanged).
When creating the new children objects, I can see that the information is correct, that it keeps them as children of the Father, etc. The information is correct, but some error in my code does not relate the children of the UpdateView with the existing ones, that's why it creates the new objects.
This is my code ( Entrada is the Father, with multiple instincts of BalaMateriesPrimeres ):
models.py
class Entrada(models.Model):
class Meta:
verbose_name_plural = 'Entrades'
data_hora = models.DateTimeField(auto_now=True)
num_factura = models.CharField(max_length=20, blank=True)
cost_total = models.DecimalField(max_digits=10, decimal_places=3, null=True, blank=True)
quilos_total = models.PositiveIntegerField(blank=True, null=True)
descripcio = models.TextField(blank=True, null=True)
observacions = models.TextField(blank=True, null=True)
proveidor = models.ForeignKey(Proveidor, related_name='entrades', on_delete=models.CASCADE)
# Metodos varios
class BalaMateriesPrimeres(models.Model):
class Meta:
verbose_name_plural = 'Bales de Materies Primeres'
magatzem = models.CharField(max_length=20, blank=True)
quilos = models.DecimalField(max_digits=6, decimal_places=2)
cost_unitari = models.DecimalField(max_digits=8, decimal_places=4)
cost_material = models.DecimalField(max_digits=8, decimal_places=4)
observacions = models.TextField(blank=True)
SI = 's'
NO = 'n'
DESCONEGUT = 'd'
PREASSIGNADA_CHOICES = (
(SI, 'Sí'),
(NO, 'No'),
(DESCONEGUT, 'Desconegut'),
)
pre_assignada = models.CharField(
max_length=1,
choices=PREASSIGNADA_CHOICES,
default=DESCONEGUT,
)
barcode = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
SI = 's'
NO = 'n'
EN_PROCES = 'p'
CONSUMIDA_CHOICES = (
(SI, 'Sí'),
(NO, 'No'),
(EN_PROCES, 'En procés'),
)
consumida = models.CharField(
max_length=1,
choices=CONSUMIDA_CHOICES,
default=NO,
)
material = models.ForeignKey('Material', related_name='materials', on_delete=models.PROTECT)
fabrica = models.ForeignKey('Fabrica', related_name='bales_materials', on_delete=models.PROTECT, blank=True, null=True)
num_entrada = models.ForeignKey(
Entrada, related_name='bales_materies_primeres', on_delete=models.CASCADE)
# Metodos varios
forms.py
class EntradaForm(ModelForm):
class Meta:
model = Entrada
exclude = ()
class BalaMateriesPrimeresForm(ModelForm):
class Meta:
model = BalaMateriesPrimeres
fields = ['quilos', 'material', 'cost_unitari', 'cost_material']
BalaMateriesPrimeresFormSet = inlineformset_factory(Entrada, BalaMateriesPrimeres, form=BalaMateriesPrimeresForm, can_delete=True, extra=1)
views.py
class EntradaUpdateView(LoginRequiredMixin, UpdateView):
model = Entrada
fields = [...]
template_name_suffix = '_update'
def get_context_data(self, **kwargs):
data = super(EntradaUpdateView, self).get_context_data(**kwargs)
# Utilizo esto para traerme los elementos hijo. Puede ser que esto sea lo que está mal?
if self.request.POST:
data['bala_form'] = BalaMateriesPrimeresFormSet(self.request.POST)
else:
data['bala_form'] = BalaMateriesPrimeres.objects.filter(num_entrada=self.kwargs['pk'])
return data
def form_valid(self, form):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
qs = BalaMateriesPrimeres.objects.filter(num_entrada=self.get_object())
formsets = BalaMateriesPrimeresFormSet(self.request.POST, queryset=qs)
if form.is_valid():
form.save()
if formsets.is_valid():
instances = formsets.save(commit=False)
for instance in instances:
instance.instance = self.object
instance.num_entrada = Entrada.objects.get(pk=self.object.id)
instance.save()
return super(EntradaUpdateView, self).form_valid(form)
With this I get to save the elements as new bullets (If you load 3 bullets, create 3 new bullets without relation to the previous ones). I've been testing for 2 days and I do not know how to do it, so I'd appreciate it if someone points me to what is failing or to some functional example with multiple Formsets.