Добавьте кнопку для набора форм django

Я начинаю использовать Django 5 и создаю веб-проект для организации обмена подарками Secret Santa.

Моя проблема заключается в том, что при использовании наборов форм они не являются динамическими, поэтому я не могу создавать переменное количество форм по запросу пользователя.

У меня такая форма:

class ParticipanteForm(forms.ModelForm):
    class Meta:
        model = Participante
        fields = ['nombre', 'email']

ParticipanteFormSet = formset_factory(ParticipanteForm)

Эта модель:

class Participante(models.Model):
    sorteo = models.ForeignKey(Sorteo, related_name='participantes', on_delete=models.CASCADE)
    nombre = models.CharField(max_length=100)
    email = models.EmailField()

А это представление, в котором я отображаю формы и сохраняю данные:


def crear_sorteo(request):
    sorteo_form = SorteoForm(request.POST or None)
    ParticipanteFormSet = formset_factory(ParticipanteForm, extra=3)
    participante_formset = ParticipanteFormSet(request.POST or None)
    
    context = {
        'sorteo_form': sorteo_form,
        'participante_formset': participante_formset,
    }
    
    if request.method == 'POST':
        if sorteo_form.is_valid():
            sorteo = sorteo_form.save()  # Save the draw first
            if participante_formset.is_valid():
                for participante_form in participante_formset:
                    participante = participante_form.save(commit=False)
                    participante.sorteo = sorteo  # Assign the draw to the participant
                    participante.save()
                return render(request, 'sorteo_realizado.html')
    
    return render(request, 'crear_sorteo.html', context)

Как видите, ParticipanteFormSet = formset_factory(ParticipanteForm, extra=3) в зависимости от того, какое количество extra вы укажете, такое количество форм и будет создано. Но я бы хотел, чтобы в шаблоне:

<body>
    <form action="" method="post">
        {% csrf_token %}
        {{ sorteo_form }}
        {{ participante_formset.management_data }} 
        {{ participante_formset.as_p }}
        <input type="submit" value="Realizar sorteo">
    </form>
</body>

должна быть кнопка для добавления участника, чтобы пользователь мог ввести столько пользователей, сколько он хочет, с минимальным количеством 3 участника.

Я пытался сделать кнопку, которая постит сообщения и при их получении количество extra обновляется до +1, но страница перезагружается и данные теряются. Если кто-то знает, как этого добиться, было бы очень полезно, заранее спасибо.

Наконец-то мне удалось решить эту проблему: В шаблоне нужно добавить следующую Django-переменную {{ participante_formset.management_form }}, которая позволяет нам работать с формами, а также добавить кнопку добавления участников, которая будет вызывать JavaScript-функцию, отвечающую за добавление форм с разными ID, чтобы они не оказались с одинаковым именем. В итоге шаблон будет выглядеть следующим образом:

<body>
    <style>
        .hidden {
            display: none;
        }
    </style>
    <form action="" method="post">
        {% csrf_token %}
        {{ sorteo_form }}
        {{ participante_formset.management_form }}
        <div id='participante-form-list'>
            {% for form in participante_formset %}
            <div class='participante-form'>
                {{ participante_formset.as_p }}
            </div>
            {% endfor %}
        </div>
        <div id='empty-form' class='hidden'>{{ participante_formset.empty_form.as_p }}</div>
        <button id='add-more' type='button'>+ participant</button>
        <input type="submit" value="Carry out draw">
    </form>
</body>

Вместе с JavaScript:

<script>
    const addMoreBtn = document.getElementById('add-more')
    const totalNewForms = document.getElementById('id_participante-TOTAL_FORMS')
    addMoreBtn.addEventListener('click', add_new_form)

    function add_new_form(event) {
        if (event) {
            event.preventDefault()
        }

        const currentParticipantes = document.getElementsByClassName('participante-form')
        let currentFormCount = currentParticipantes.length

        const formCopyTarget = document.getElementById('participante-form-list')
        const copyEmptyFormEL = document.getElementById('empty-form').cloneNode(true)
        copyEmptyFormEL.setAttribute('class', 'participante-form')
        copyEmptyFormEL.setAttribute('id', `form-${currentFormCount}`)
        const regex = new RegExp('__prefix__', 'g')
        copyEmptyFormEL.innerHTML = copyEmptyFormEL.innerHTML.replace(regex,
        currentFormCount)
        totalNewForms.setAttribute('value', currentFormCount + 1)

        formCopyTarget.append(copyEmptyFormEL)
    }
</script>

Наконец, в представлении нам просто нужно объявить набор форм с дополнительным значением 0:

ParticipanteFormSet = formset_factory(ParticipanteForm, extra=0)

Я пришел к этому коду благодаря следующему видео, которое, несомненно, будет очень полезным для вас, как и для меня:

https://youtu.be/s3T-w2jhDHE?si=agQ07IQGP__QAitk

Вернуться на верх