Despite using Django for a number of years, I haven’t really worked with Django’s Forms until this week. I needed to create a form to handle a file upload, which has an associated category. The form also had to allow users to create new categories from within the file. When creating a new category, we have some extra data we want associated with the new category. I learned a few things trying to set this up.
First, forms.ModelForm: The fields on this form don't have to map 1-1 to the model. The upload model has a category field, but we need some extra fields on the form when we create new categories that don't exist on the upload model. No problem! I thought I might have to fall back to a standard form since I needed extra fields, but I was able to add them to the form and assign them to the category in the view.
Also, to break up and style different fields in a form, you can individually identify the fields in the template file and style them. So instead of
<formid="id_upload_form" action="{% url 'app_name:upload_file' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" id="idupload_form_submit" class="btn btn-primary mt-4 disabled" value="Upload">
</form>
We have:
<formid="id_upload_form" action="{% url 'app_name:upload_file' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.file|as_crispy_field }}
{{ form.category|as_crispy_field }}
{{ form.new_category_sources|as_crispy_field }}
{{ form.new_category_display_name|as_crispy_field }}
<input type="submit" id="idupload_form_submit" class="btn btn-primary mt-4 disabled" value="Upload">
</form>
This allowed me to wrap the bottom two fields in a Boostrap accordion in order to hide them from users who are updating a category instead of creating one.
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
Create New Category
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body">
{{ form.new_category_sources|as_crispy_field }}
{{ form.new_category_display_name|as_crispy_field }}
</div>
</div>
</div>
We already have bootstrap as a dependency, so I like this solution better than adding JavaScript, which I also saw as a way to solve this problem.