Hello, good I am developing a page in Django where a user fills in the data of a draw in a form. This data is saved in a MySQL database. Part of the functionality of the page is that the user can retrieve the form with the data already entered for the draw to add more, modify some, or relaunch the draw again without having to re-enter all the data.
Now for this, I have these forms that I am using to retrieve the draw, add participants, add draw data, etc…
class DrawForm(forms.ModelForm):
class Meta:
model = Draw
fields = ['organizer', 'celebration_date', 'budget']
class DrawRetrieverForm(forms.Form):
drawRetriever = forms.CharField(label='Draw code', max_length=50, required=False)
class ParticipantExclusionForm(forms.Form):
name = forms.CharField(label='', max_length=100, widget=forms.TextInput(attrs={'placeholder': 'Name'}))
email = forms.EmailField(label='', max_length=100, widget=forms.EmailInput(attrs={'placeholder': 'Email'}))
exclusions = forms.CharField(label='', max_length=100, required=False, widget=forms.TextInput(attrs={'placeholder': 'Exclusions'}))
ParticipantExclusionFormSet = formset_factory(ParticipantExclusionForm)
ParticipantFormSet = formset_factory(ParticipantForm)
The data control of these forms, both to relaunch the draw and to retrieve the data, is done from this method where it is checked which form is which:
def retrieve_draw(request):
draw_form = DrawForm(request.POST or None)
ParticipantExclusionFormSet = formset_factory(ParticipantExclusionForm, extra=0)
participant_formset = ParticipantExclusionFormSet(request.POST or None, prefix='participant')
retriever_form = DrawRetrieverForm(request.POST or None)
draw = None
participants = None
if request.method == 'POST':
if 'retrieve_draw' in request.POST:
if retriever_form.is_valid():
# Retrieve the draw and participants
draw = Draw.objects.filter(retrieveDraw=retriever_form.cleaned_data['drawRetriever']).first()
if draw:
participants = draw.participants.all()
if 'perform_draw' in request.POST:
if draw_form.is_valid() and participant_formset.is_valid():
#Logic to save the draw
context = {
'draw_form': draw_form,
'participant_formset': participant_formset,
'retriever_form': retriever_form,
'draw': draw,
'participants': participants,
}
return render(request, 'index/retrieve_draw.html', context)
All this is managed in the following template:
<form method="post" onsubmit="validateForm()">
{% csrf_token %}
{{ retriever_form }}
<button type="submit" name="retrieve_draw">Retrieve</button>
</form>
{% if draw %}
<form action="" method="post" class="completeForm">
{% csrf_token %}
<div class="drawForm">
<div>
<label for="organizer">Organizer Name</label>
{{ draw_form.organizer }}
</div>
<div>
<label for="celebration_date">Celebration Date</label>
{{ draw_form.celebration_date }}
</div>
<div>
<label for="budget">Budget</label>
{{ draw_form.budget }}
</div>
</div>
<h3 class="copyForm">PARTICIPANTS</h3>
<div class="participants-header">
<h4>Name</h4>
<h4>Email</h4>
<h4>Exclusions</h4>
</div>
{{ participant_formset.management_form }}
<div id='participant-form-list'>
{% for form in participant_formset %}
<div class='participant-form'>
{{ participant_formset.as_p }}
</div>
{% endfor %}
</div>
<div id='empty-form-participant' class='hidden'>{{ participant_formset.empty_form.as_p }}</div>
<div>
<button class="formButtons addButton" id='add-more' type='button'>Add participant</button>
<input class="formButtons doneButton" type="submit" value="Perform draw" name="perform_draw">
</div>
</form>
{% endif %}
With its corresponding JavaScript:
<script>
function validateForm() {
var field = document.getElementById('id_drawRetriever').value;
if (field.trim() == '') {
alert('Please complete the field');
return false;
}
return true;
}
/* Logic to load the data into the form */
function start() {
document.getElementById("id_organizer").value = "{{ draw.organizer }}"
document.getElementById("id_celebration_date").value = "{{ draw.celebration_date }}"
document.getElementById("id_budget").value = "{{ draw.budget }}"
};
start()
/* Complete logic for the draw form */
const addMoreBtn = document.getElementById('add-more');
const totalNewFormsParticipant = document.getElementById('id_participant-TOTAL_FORMS');
addMoreBtn.addEventListener('click', add_new_form);
for (let index = 0; index < 3; index++) { addMoreBtn.click() }
function add_new_form(event) {
if (event) {
event.preventDefault();
}
const currentParticipants = document.getElementsByClassName('participant-form');
let currentFormCountParticipants = currentParticipants.length;
const formCopyTarget = document.getElementById('participant-form-list');
const copyEmptyFormELParticipant = document.getElementById('empty-form-participant').cloneNode(true);
copyEmptyFormELParticipant.setAttribute('class', 'participant-form');
copyEmptyFormELParticipant.setAttribute('id', `form-${currentFormCountParticipants}`);
const regex = new RegExp('__prefix__', 'g');
copyEmptyFormELParticipant.innerHTML = copyEmptyFormELParticipant.innerHTML.replace(regex, currentFormCountParticipants);
totalNewFormsParticipant.setAttribute('value', currentFormCountParticipants + 1);
formCopyTarget.append(copyEmptyFormELParticipant);
}
</script>
When executing this within the form, I get the errors:
(Hidden field TOTAL_FORMS) *This field is required.
(Hidden field INITIAL_FORMS) *This field is required.
Tests and hypotheses
- Deactivating the if statements in the template removes the error until the draw code to be retrieved is entered.
- Leaving only the form for the draw data and not retrieving the participants/exclusions, the error does not appear.
- Check if the TOTAL_FORMS and INITIAL_FORMS fields exist and are rendered correctly.
- In the template where the user enters the draw data for the first time, the error does not appear.
- Perhaps the issue is having 2 forms in the same view; I tried to use a button with a different name to control it in the view.
When the page load
When the page loads, upon inspecting the source code, you can observe the supposed fields that are not present, attached the code:
<!-- Header -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/static/sorteo/css/style.css">
</head>
<body>
<nav class="navbar">
<div class="containerNavbar">
<!-- Logo on the left -->
<a class="navbar-brand" href="/">
<img src="/static/sorteo/images/LogoAmigoInvisible.webp" alt="Logo">
</a>
<!-- Buttons on the right -->
<div class="navbar-nav">
<button class="button-navbar-recuperarSorteo" onclick="location.href='recuperar-sorteo/'">Retrieve Draw</button>
<button class="button-navbar-findder" onclick="location.href='https://www.findder.es/'">By findder</button>
</div>
</div>
</nav>
<h2> Enter the draw code </h2>
<p>Enter the draw code that you can find in any of the emails sent to the participants and any participant's email</p>
<form method="post" onsubmit="validateForm()">
<input type="hidden" name="csrfmiddlewaretoken" value="8Jrhl9K8GU2TeJhgFweZwrKr1hWa6b5I037gwEVbbis7IWtcMNzR5DNIA9HVnNY9">
<tr>
<th><label for="id_sorteoRecuperador">Draw code:</label></th>
<td>
<input type="text" name="sorteoRecuperador" value="08f4d9cfc3794c9381eee31232b28edb" maxlength="50" id="id_sorteoRecuperador">
</td>
</tr>
<button type="submit" name="retrieve_draw">Retrieve</button>
</form>
<form action="" method="post" class="completeForm">
<input type="hidden" name="csrfmiddlewaretoken" value="8Jrhl9K8GU2TeJhgFweZwrKr1hWa6b5I037gwEVbbis7IWtcMNzR5DNIA9HVnNY9">
<div class="drawForm">
<div>
<label for="organizer">Organizer Name</label>
<input type="text" name="organizer" maxlength="100" required id="id_organizer">
</div>
<div>
<label for="celebration_date">Celebration Date</label>
<input type="text" name="celebration_date" required id="id_celebration_date">
</div>
<div>
<label for="budget">Budget</label>
<input type="number" name="budget" step="0.01" required id="id_budget">
</div>
</div>
<h3 class="copyForm">PARTICIPANTS</h3>
<div class="participants-header">
<h4>Name</h4>
<h4>Email</h4>
<h4>Exclusions</h4>
</div>
<ul class="errorlist nonfield"><li>(Hidden field TOTAL_FORMS) *This field is required.</li><li>(Hidden field INITIAL_FORMS) *This field is required.</li></ul>
<div><input type="hidden" name="participant-TOTAL_FORMS" id="id_participant-TOTAL_FORMS"><input type="hidden" name="participant-INITIAL_FORMS" id="id_participant-INITIAL_FORMS"><input type="hidden" name="participant-MIN_NUM_FORMS" id="id_participant-MIN_NUM_FORMS"><input type="hidden" name="participant-MAX_NUM_FORMS" id="id_participant-MAX_NUM_FORMS"></div>
<div id='participant-form-list'>
</div>
<div id='empty-form-participant' class='hidden'><p>
<input type="text" name="participant-__prefix__-name" placeholder="Name" maxlength="100" id="id_participant-__prefix__-name">
</p>
<p>
<input type="email" name="participant-__prefix__-email" placeholder="Email" maxlength="100" id="id_participant-__prefix__-email">
</p>
<p>
<input type="text" name="participant-__prefix__-exclusions" placeholder="Exclusions" maxlength="100" id="id_participant-__prefix__-exclusions">
</p></div>
<div>
<button class="formButtons addButton" id='add-more' type='button'>Add participant</button>
<input class="formButtons doneButton" type="submit" value="Perform draw" name="perform_draw">
</div>
</form>
<footer class="containerFooter">
<div class="SocialNetworks">
<p>info@sorteoamigoinvisible.es</p>
<a href="https://www.instagram.com/findder.es/ " class="logo-instagram-icon"></a>
<a href="https://es.linkedin.com/company/findder-es" class="logo-linkedin-icon"></a>
<a href="https://www.tiktok.com/@findder.es" class="logo-tiktok-icon"></a>
<a href="https://findder.es/" class="logo-finddy-icon"></a>
</div>
<div class="Policies">
<a href="findder.es">© 2024 Findder</a>
<a href="about.html">Legal notice</a>
<a href="contact.html">Cookie policy</a>
<a href="terms.html">Privacy policy</a>
<a href="terms.html">More info</a>
</div>
</footer>
</body>
<script>
function validateForm() {
var field = document.getElementById('id_drawRetriever').value;
if (field.trim() == '') {
alert('Please complete the field');
return false;
}
return true;
}
/* Logic to load the data into the form */
function start() {
document.getElementById("id_organizer").value = "Technical Team"
document.getElementById("id_celebration_date").value = "May 19, 2024"
document.getElementById("id_budget").value = "20.00"
};
start()
/* Complete logic for the draw form */
const addMoreBtn = document.getElementById('add-more');
const totalNewFormsParticipant = document.getElementById('id_participant-TOTAL_FORMS');
addMoreBtn.addEventListener('click', add_new_form);
for (let index = 0; index < 3; index++) {
addMoreBtn.click()
}
function add_new_form(event) {
if (event) {
event.preventDefault();
}
const currentParticipants = document.getElementsByClassName('participant-form');
let currentFormCountParticipants = currentParticipants.length;
const formCopyTarget = document.getElementById('participant-form-list');
const copyEmptyFormELParticipant = document.getElementById('empty-form-participant').cloneNode(true);
copyEmptyFormELParticipant.setAttribute('class', 'participant-form');
copyEmptyFormELParticipant.setAttribute('id', `form-${currentFormCountParticipants}`);
const regex = new RegExp('__prefix__', 'g');
copyEmptyFormELParticipant.innerHTML = copyEmptyFormELParticipant.innerHTML.replace(regex, currentFormCountParticipants);
totalNewFormsParticipant.setAttribute('value', currentFormCountParticipants + 1);
formCopyTarget.append(copyEmptyFormELParticipant);
}
</script>
</html>
This is actually crazy becouse the field exist but django doesnt reconize it