Handle Multiple Forms on a Single Page in Django
Last Updated :
02 Jul, 2025
One common scenario in web development is handling multiple forms on a single page. This might be required when a user needs to fill out various types of information without navigating away from the page. In this article, we will explore the best practices for managing multiple forms on a single page in Django, including structuring our HTML template, handling form submissions, and validating user input.
When dealing with multiple forms on a single page, it's crucial to understand how Django's forms system can handle multiple instances simultaneously. Each form should be uniquely identified, and we need to ensure that the server correctly processes each form's data. Typically, forms are used for different purposes, such as creating or updating records. Properly distinguishing between these forms is essential for accurate data processing.
Step-by-Step Integration :
Let's create a Django project that handles multiple forms with a creative and functional design. We’ll include two forms with different fields, ensure that they are styled nicely, and handle form submissions effectively.
1. Create a Django Project and App:
django-admin startproject myproject
cd myproject
python manage.py startapp myapp
Add myapp to the INSTALLED_APPS in settings.py:
Python
# ...
INSTALLED_APPS = [
# ... other installed apps
'myapp',
]
And add the following piece of code too in "settings.py" to link css to the project:
Python
# settings.py
STATIC_URL = '/static/'
# Make sure this is set correctly
STATICFILES_DIRS = [BASE_DIR / "myapp/static"]
# This tells Django to serve the static files during development
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
The project Structure should look something like this:
fig: File StructureNote: We will add the html file later in the templates directory.
2. Define Models
In myapp/models.py file, let's create two models:
Python
# myapp/models.py
from django.db import models
class Form1Model(models.Model):
field1 = models.CharField(max_length=100)
field2 = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.field1} - {self.field2}"
class Form2Model(models.Model):
field3 = models.CharField(max_length=100)
field4 = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.field3} - {self.field4}"
To apply these models to the database let's run migrations and migrate commands:
python manage.py makemigrations
python manage.py migrate
In 'myapp/forms.py', let's create two forms that will take input strings of minimum length 5:
Python
from django import forms
from .models import Form1Model, Form2Model
class Form1(forms.ModelForm):
class Meta:
model = Form1Model
fields = ['field1', 'field2']
def clean_field1(self):
field1 = self.cleaned_data.get('field1')
if len(field1) < 5:
raise forms.ValidationError("Field 1 must be at least 5 characters long.")
return field1
class Form2(forms.ModelForm):
class Meta:
model = Form2Model
fields = ['field3', 'field4']
def clean_field3(self):
field3 = self.cleaned_data.get('field3')
if len(field3) < 5:
raise form
In 'myapp/views.py', define a view that handles both forms:
Python
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import Form1, Form2
from .models import Form1Model, Form2Model
def handle_forms(request):
form1 = Form1(prefix='form1')
form2 = Form2(prefix='form2')
if request.method == 'POST':
if 'submit_form1' in request.POST:
form1 = Form1(request.POST, prefix='form1')
if form1.is_valid():
form1.save()
messages.success(request, 'Form 1 submitted successfully.')
return redirect('handle-forms')
# Keep form2 unchanged if form1 is submitted
elif 'submit_form2' in request.POST:
form2 = Form2(request.POST, prefix='form2')
if form2.is_valid():
form2.save()
messages.success(request, 'Form 2 submitted successfully.')
return redirect('handle-forms')
# Keep form1 unchanged if form2 is submitted
form1_data = Form1Model.objects.all()
form2_data = Form2Model.objects.all()
return render(request, 'myapp/forms_template.html', {
'form1': form1,
'form2': form2,
'form1_data': form1_data,
'form2_data': form2_data,
})
Create a template 'myapp/templates/myapp/my_template.html' and create a 'static/css/styles.css' file in 'myapp' directory to display and handle form submissions:
HTML
<!-- myapp/templates/myapp/my_template.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multiple Forms</title>
{% load static %}
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body>
<div class="container">
<h1>Form 1</h1>
<form method="post" class="form">
{% csrf_token %}
{{ form1.as_p }}
{% if form1.errors %}
<div class="error">
<p>Please correct the errors below:</p>
<ul>
{% for error in form1.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<button type="submit" class="btn">Submit Form 1</button>
</form>
<h2>Form 1 Data</h2>
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>Field 1</th>
<th>Field 2</th>
<th>Created At</th>
</tr>
</thead>
<tbody>
{% for item in form1_data %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.field1 }}</td>
<td>{{ item.field2 }}</td>
<td>{{ item.created_at }}</td>
</tr>
{% empty %}
<tr>
<td colspan="4">No data available</td>
</tr>
{% endfor %}
</tbody>
</table>
<h1>Form 2</h1>
<form method="post" class="form">
{% csrf_token %}
{{ form2.as_p }}
{% if form2.errors %}
<div class="error">
<p>Please correct the errors below:</p>
<ul>
{% for error in form2.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<button type="submit" class="btn">Submit Form 2</button>
</form>
<h2>Form 2 Data</h2>
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>Field 3</th>
<th>Field 4</th>
<th>Created At</th>
</tr>
</thead>
<tbody>
{% for item in form2_data %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.field3 }}</td>
<td>{{ item.field4 }}</td>
<td>{{ item.created_at }}</td>
</tr>
{% empty %}
<tr>
<td colspan="4">No data available</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
CSS
/* myapp/static/styles.css */
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
width: 80%;
margin: 0 auto;
padding: 20px;
background: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
margin-top: 50px;
}
h1 {
color: #333;
margin-bottom: 20px;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.form {
margin-bottom: 30px;
}
.btn {
padding: 10px 20px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
.btn:hover {
background-color: #0056b3;
}
input[type="text"], textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 16px;
}
/* Existing styles */
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.data-table th, .data-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.data-table th {
background-color: #f4f4f4;
}
body {
font-family: Arial, sans-serif;
}
.container {
width: 80%;
margin: auto;
padding: 20px;
}
.form {
margin-bottom: 30px;
}
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.data-table th, .data-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.data-table th {
background-color: #f2f2f2;
}
.btn {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
.error {
color: red;
margin-top: 10px;
}
Define the static folder in the 'settings.py' file:
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
If there is no "urls.py" file in "myapp" folder then create one to define the URLs of our view. Also connect our view to a URL pattern in 'myproject/urls.py'.
Python
# myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('handle-forms/', views.handle_forms, name='handle-forms'),
]
Add the following code in "myproject/urls.py":
Python
#myproject/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('myapp.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
6. Run the Server:
Make sure everything is correctly set up and then run your Django development server:
python manage.py runserver
Navigate to 'http://127.0.0.1:8000/handle-forms/' to see multiple forms in action with a more polished interface.
Expected Output:
fig: Form view
fig: Filling data in form1
fig: Output of Form1
fig: filling data in form2
fig: Output of Form2Other way around would be to handle form submission using JavaScript. This would prevent page reloading when submitting the data.
Related articles: