I have the following problem: I’m loading a form(Form A “Evento”) for the User of application fill, in that Form I have to load dynamically another form(Form B “Logistica”), so that the User can add several Form B just clicking the button. I managed to load dynamically these Logistica/Form B for the User, but when I go to the saving part I only managed to receive the fields in a tuple, for example the Form B has a field called “nome”, instead of receiving N FormB “nome” fields, I receive only 1 with a [] with the values of each Form. I realize that Django is thinking that instead of N FormB, I have only 1 FormB and it is concatenating the fields
Problem https://github.com/ian-santos-nascimento/Django-event-management
This is my form
<form method="post" role="form" action="{% url 'evento_create' %}">
{% csrf_token %}
<div class="form-group">
<label for="{{ form.nome.id_for_label }}">Nome</label>
{{ form.nome }}
</div>
<div class="form-group">
<label for="{{ form.descricao.id_for_label }}">Descrição</label>
{{ form.descricao }}
</div>
<div class="form-group">
<label for="{{ form.observacao.id_for_label }}">Observação</label>
{{ form.observacao }}
</div>
<div class="row">
<div class="col">
<label for="{{ form.local.id_for_label }}">Local</label>
{{ form.local }}
</div>
<div class="col">
<label for="{{ form.local.id_for_label }}">Cliente</label>
{{ form.cliente }}
</div>
</div>
<div class="form-group mt-3">
<label>Comidas</label>
<div style="height: 30vh;overflow-y: scroll; border: 1px solid #ced4da">
{% for comida in comidas %}
<div class="pl-3">
<input type="checkbox" name="comidas" value="{{ comida.comida_id }}"
class="form-check-input" id="comida-{{ comida.comida_id }}">
<label class="form-check-label" for="comida-{{ comida.comida_id }}">{{ comida.nome }} --
R${{ comida.valor }}</label>
<div class="quantidade-container" style="display: inline-block; margin-left: 10px;"></div>
</div>
{% endfor %}
</div>
</div>
<div class="row">
<div class="col">
<label for="{{ form.data_inicio.id_for_label }}">Data de Início</label>
{{ form.data_inicio }}
</div>
<div class="col">
<label for="{{ form.data_fim.id_for_label }}">Data de Fim</label>
{{ form.data_fim }}
</div>
</div>
<h2 id="logistica-title">Logistica</h2>
{{ logistica_formset.management_form }}
<div id="logisticas-forms">
{% for form in logistica_formset %}
<div class="logistica-form">
{{ form|crispy }}
<button type="button" class="remove-logistica btn btn-danger btn-sm mb-3">Remover</button>
</div>
{% endfor %}
</div>
<button type="button" class="btn btn-info btn-sm pl-0" id="add-logistica">Adicionar logistica
</button>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<script>
const addLogisticaButton = document.getElementById('add-logistica');
const logisticasForms = document.getElementById('logisticas-forms');
const emptyForm = logisticasForms.lastElementChild.cloneNode(true);
const logisticaTitle = document.getElementById('logistica-title');
const totalFormsInput = document.getElementById('id_logistica-TOTAL_FORMS');
let formCount = 1;
addLogisticaButton.addEventListener('click', () => {
const newForm = emptyForm.cloneNode(true);
newForm.style = 'margin-bottom: 30px';
newForm.innerHTML = newForm.innerHTML.replace(/__prefix__/g, formCount);
console.log('emptyForm:', emptyForm);
console.log('newForm.innerHTML:', newForm.innerHTML);
logisticasForms.appendChild(newForm);
updateLogisticaTitleVisibility();
totalFormsInput.value = logisticasForms.children.length;
formCount++;
});
logisticasForms.addEventListener('click', (event) => {
if (event.target.classList.contains('remove-logistica')) {
event.target.parentElement.remove();
updatelogisticaTitleVisibility();
totalFormsInput.value = logisticasForms.children.length;
}
});
function updatelogisticaTitleVisibility() {
if (logisticasForms.children.length === 0) {
logisticaTitle.style.display = 'none';
} else {
logisticaTitle.style.display = 'block';
}
}
updatelogisticaTitleVisibility();
</script>
This is my views for the management of that Form
@login_required(login_url='login')
def home(request):
if request.method == 'POST':
return evento_create_form(request)
eventos = Evento.objects.only('nome', 'data_inicio', 'local', 'id_evento')
return render(request, 'main/home.html', {'eventos': eventos})
@login_required(login_url='login')
def evento_create(request):
if request.method == 'POST':
form = CreateEventoForm(request.POST)
logistica_formset = modelformset_factory(Logistica, CreateLogisticaForm,
fields=('nome', 'descricao', 'valor', 'dias', 'tipo'),
extra=request.POST['logistica-TOTAL_FORMS'], )
logistica_forms = logistica_formset(request.POST, prefix='logistica')
print(request.POST)
if logistica_forms.is_valid():
print(logistica_forms.errors)
for logistica in logistica_forms:
print('LOGISTICA'+logistica)
if form.is_valid():
evento = form.save(commit=False)
comidas = criarComidasEvento(form)
for comida_id, quantidade in comidas.items():
comida = Comida.objects.get(pk=comida_id)
evento.save()
evento.comidas.add(comida, through_defaults={'valor': comida.valor, 'quantidade': quantidade})
messages.success(request, 'Evento salvo com sucesso!')
return HttpResponseRedirect(reverse('home'))
else:
message_error(request, form)
return render(request, 'eventos/novoEvento.html', {'form': form})
else:
form = CreateEventoForm()
return render(request, 'eventos/novoEvento.html', {'form': form, })
def evento_create_form(request):
form = CreateEventoForm()
logistica_formset = modelformset_factory(Logistica, CreateLogisticaForm, extra=2)
logistica_form = logistica_formset(prefix='logistica')
comidas = Comida.objects.all()
return render(request, 'eventos/novoEvento.html',
{'form': form, 'comidas': comidas, 'logistica_formset': logistica_form})
These are my two forms
class CreateLogisticaForm(forms.ModelForm):
class Meta:
model = Logistica
fields = '__all__'
exclude = ['evento_id']
widgets = {
'tipo': forms.Select(),
}
labels = {
'valor': "Valor diária",
'dias': "Qtd dias"
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.empty_permitted = True
class CreateEventoForm(forms.ModelForm):
local = forms.ModelChoiceField(queryset=LocalEvento.objects.all().only('nome').order_by('nome'),
widget=forms.Select(attrs={'class': 'form-control'}))
data_inicio = forms.DateField(widget=forms.SelectDateWidget(attrs={'class': 'mt-3 mb-3 col-3'}))
data_fim = forms.DateField(widget=forms.SelectDateWidget(attrs={'class': 'mt-3 mb-3 col-3'}))
cliente = forms.ModelChoiceField(queryset=Cliente.objects.all().only('nome').order_by('nome'),
label='Cliente',
widget=forms.Select(attrs={'class': 'form-control'}))
class Meta:
model = Evento
exclude = ['id_evento']
fields = ['nome', 'descricao', 'observacao', 'comidas', 'data_inicio', 'local', 'cliente',
'data_fim']
widgets = {
'nome': forms.TextInput(attrs={'class': 'form-control'}),
'descricao': forms.Textarea(attrs={'class': 'form-control', 'style': 'height: 200px;'}),
'observacao': forms.Textarea(attrs={'class': 'form-control', 'style': 'height: 200px;'}),
'qtd_dias_evento': forms.NumberInput(attrs={'class': 'form-control'}),
}
And finally my model
class Evento(models.Model):
id_evento = models.AutoField(primary_key=True)
nome = models.CharField(max_length=100)
descricao = models.TextField()
observacao = models.TextField()
comidas = models.ManyToManyField(Comida, through='ComidaEvento')
qtd_dias_evento = models.IntegerField(null=True, blank=True)
local = models.ForeignKey(LocalEvento, on_delete=models.DO_NOTHING)
data_inicio = models.DateField(null=True, blank=True)
data_fim = models.DateField(null=True, blank=True)
def save(self):
dias = self.data_fim - self.data_inicio
self.qtd_dias_evento = dias.days
super(Evento, self).save()
class Logistica(models.Model):
id_logistica = models.AutoField(primary_key=True)
nome = models.CharField(max_length=200)
descricao = models.TextField()
valor = models.DecimalField(decimal_places=2, max_digits=6)
dias = models.IntegerField()
tipo = models.CharField(max_length=20, choices=listSelect.TIPO_LOGISTICA)
evento_id = models.ForeignKey(Evento, on_delete=models.CASCADE, default=None)
def __str__(self):
return self.nome