带有 Django Crispy Forms 的空表单集 CSS

empty formset CSS with Django Crispy Forms

我正在尝试使用 add-more 表单按钮在 Django 中呈现带有脆皮表单的表单集。但是我无法弄清楚如何将相同的 CSS 应用于原始表格的空表格。我成功的唯一方法是创建一个新的空表单,如 this solution,但这会添加另一个 <form> 标记,这是解决方案中未涵盖的问题。如何将 CSS 应用于动态添加的表单集?

下图是我点击添加更多成分后的预期结果<button>:

forms.py

from .models import Recipe, RecipeIngredient
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Div, Layout, Field


class RecipeIngredientForm(forms.ModelForm):
    class Meta:
        model = RecipeIngredient
        fields = ['name', 'quantity', 'unit', 'description']
        labels = {
            'name': "Ingredient",
            "quantity:": "Ingredient Quantity",
            "unit": "Unit",
            "description:": "Ingredient Description"}
        

    def __init__(self, *args, **kwargs):
        super(RecipeIngredientForm, self).__init__(*args, **kwargs)

        self.helper = FormHelper()
        self.helper.form_id = 'id-entryform'
        self.helper.form_class = 'form-inline'
        self.helper.layout = Layout(
            Div(
                Div(Field("name", placeholder="Chickpeas"), css_class='col-6 col-lg-4'),
                Div(Field("quantity", placeholder="2 x 400"), css_class='col-6 col-md-4'),
                Div(Field("unit", placeholder="grams"), css_class='col-5 col-md-4'),
                Div(Field("description", placeholder="No added salt tins"), css_class='col-12'),
            
            css_class="row",
           ),
           
        )

views.py:

def recipe_create_view(request):
    form = RecipeForm(request.POST or None)
    RecipeIngredientFormset = formset_factory(RecipeIngredientForm, prefix="ingredient")
    formset = RecipeIngredientFormset(request.POST or None)
    
    context = {
        "form": form,
        "formset": formset,
    }
    if request.method == "POST":
        #print(request.POST)
        if form.is_valid() and formset.is_valid():
            parent = form.save(commit=False)
            parent.user = request.user
            parent.save()
            
            #recipe ingredients
            for form in formset:
                child = form.save(commit=False)
                if form.instance.name.strip() == '':
                    pass
                else:
                    child.recipe = parent
                    child.save()
            
    else:
        form = RecipeForm(request.POST or None)
        formset = RecipeIngredientFormset()
        
    return render(request, "recipes/create.html", context)

create.html

{% load crispy_forms_tags %}

<form action='/recipes/create/' method="POST" >
    <!-- MAIN RECIPE FORM-->
    {% csrf_token %}
    <div class='row'>
        {% for recipe in form %}   
        <div class="col-6 col-md-3 py-2">
            {{ recipe|as_crispy_field }}
        </div>
        {% endfor %}
    <div>
        <!--RECIPE INGREDIENTS - WHERE CSS ISSUE IS --> 
        {% if formset %}
            <h3>Ingredients</h3>
            {{ formset.management_form|crispy }}
            
            <div id='ingredient-form-list'>
                {% for ingredient in formset %}
               
                        <div class='ingredient-form'>
                            
                            {% crispy ingredient %}
                            
                        </div>
                {% endfor %}
            </div>

            <div id='empty-form' class='hidden'>{% crispy formset.empty_form %}</div>
            <button class="btn btn-success" id='add-more' type='button'>Add more ingredients</button>
        {% endif %}
        </div>

<script>
    //ingredients add form
    const addMoreBtn = document.getElementById('add-more')
    const totalNewForms = document.getElementById('id_ingredient-TOTAL_FORMS')
    
    addMoreBtn.addEventListener('click', add_new_form)
    function add_new_form(event) {
        if (event) {
            event.preventDefault()
        }
        const currentIngredientForms = document.getElementsByClassName('ingredient-form')
        const currentFormCount = currentIngredientForms.length // + 1
        const formCopyTarget = document.getElementById('ingredient-form-list')
        const copyEmptyFormEl = document.getElementById('empty-form').cloneNode(true)
        copyEmptyFormEl.setAttribute('class', 'ingredient-form')
        copyEmptyFormEl.setAttribute('id', `ingredient-${currentFormCount}`)
        const regex = new RegExp('__prefix__', 'g')
        copyEmptyFormEl.innerHTML = copyEmptyFormEl.innerHTML.replace(regex, currentFormCount)
        totalNewForms.setAttribute('value', currentFormCount + 1)
        // now add new empty form element to our html form
        formCopyTarget.append(copyEmptyFormEl)
    }
</script>

问题如下图所示,这会创建另一个表单,而不仅仅是添加字段:

如果我使用 {{ formset.empty_form|crispy }} 而不是 {% crispy formset.empty_form %} 脚本可以正常工作,但是 CSS 没有附加到表单。

必须结合使用这两种方法才能工作,下面是对问题代码的更改:

create.html

<div>
                <!--RECIPE INGREDIENTS-->
                {% if formset %}
                    <h3 class="mt-4 mb-3">Ingredients</h3>
                    {{ formset.management_form|crispy }}
                    
                    <div id='ingredient-form-list'>
                        {% for ingredient in formset %}
                    
                                <div class='ingredient-form'>
                                    
                                    {% crispy ingredient %}
                                    
                                </div>
                        {% endfor %}
                    </div>

                    <div id='empty-form' class='hidden'>
                        <div class="row mt-4">
                            <div class="col-6">{{ formset.empty_form.name|as_crispy_field }}</div>
                            <div class="col-6">{{ formset.empty_form.quantity|as_crispy_field }}</div>
                            <div class="col-6">{{ formset.empty_form.unit|as_crispy_field }}</div>
                          
                        </div>
                    </div>
                    <button class="btn btn-success my-2" id='add-more' type='button'>Add more ingredients</button>
                {% endif %}
            </div>
        <div>