In this article, I'm going to explain everything that you need to know about multiple image uploading. There are few techniques for multiple image uploads. According to your requirements, you can choose any one technique from those three. Let's start...
I assume that you know the basics of Django, like project and app creation models, views, forms, etc.
Case study for multiple image uploads:
- Multiple images with the same title
- Multiple images with their own title: It's like, with one click, uploading multiple product details. Assume product name, description, and thumbnail.
- Multiple images related to one object: In e-commerce, products have multiple preview images. You want to upload a product name and description with a list of preview images.
Those three cases with codes are below.
Project folder structure:
This is a demo project structure. I developed it for you to write this article. It is for your understanding about template directories. Just notice here the project-level template directory and the app-level template directory position. Also, you should know how the template directory is registered with the project.
Register the template directory in settings.py. As a result, the parentlevel 'templates' directory and the app-level 'templates' directory will be able to serve HTML files as django-templates.
import os
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "templates")],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
Also, configure media and static files in settings.py.
STATIC_URL = "/static/"
MEDIA_URL = "/media/"
STATIC_ROOT = os.path.join(BASE_DIR, "static")
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
Upload multiple images with the same title:
Model:
from django.db import models
class MultipleImage(models.Model):
title = models.CharField(max_length=255)
images = models.ImageField(upload_to='multiple_images/', blank=True, null=True)
Form:
from django import forms
from .models import MultipleImage
class MultipleImageForm(forms.ModelForm):
class Meta:
model = MultipleImage
fields = ['title', 'images']
widgets = {'images': forms.ClearableFileInput(attrs={'multiple': True})}
View
from django.shortcuts import render
from .forms import MultipleImageForm
def upload_multiple_image(request):
message = None
if request.method == 'POST':
form = MultipleImageForm(request.POST, request.FILES)
if form.is_valid():
for img in request.FILES.getlist('images'):
form = MultipleImageForm({'title': request.POST['title']}, {'images': img})
if form.is_valid():
form.save()
message = 'Images uploaded successfully!'
else:
form = MultipleImageForm()
context = {'form': form, 'message': message}
return render(request, 'upload_multiple_image.html', context)
Template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multiple Image Upload</title>
</head>
<body>
<h2>Upload Multiple Images</h2>
{% if message %}
<p>{{ message }}</p>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
</body>
</html>
Configure URL:
from django.urls import path
from .views import upload_multiple_image
urlpatterns = [
path('upload-multiple/', upload_multiple_image, name='upload_multiple_image'),
]
Output
Multiple images with their own title
Here, the model and form will be the same. We just need to change the view and template. So for demonstration purposes, I created a new view, template, and URL.
Template:
Create a new file named upload_multiple_with_own_title_image.html. Here, you can upload multiple images with their own titles. But for demonstration purposes, I limited myself to three images with titles. But you can upload it as per your requirements.
For uploading 'n' numbers of images, you should make a dynamic form using Javascript. I assume the user can upload a maximum three images with their own title.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multiple Image Upload</title>
</head>
<body>
<h2>Upload Multiple Images with own title</h2>
{% if message %}
<p>{{ message }}</p>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<br>
{{ form.as_p }}
<br>
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
</body>
</html>
Here I used same form three times ({{ form.as_p }}
). So that you can upload maximum three data.
View:
Create a new view named upload_multiple_with_own_title_image.
from .models import MultipleImage
def upload_multiple_with_own_title_image(request):
message = None
if request.method == 'POST':
form = MultipleImageForm(request.POST, request.FILES)
if form.is_valid():
for title, img in zip(request.POST.getlist('title'), request.FILES.getlist('images')):
MultipleImage.objects.create(title=title, images=img)
message = 'Images uploaded successfully!'
else:
form = MultipleImageForm()
return render(request, 'upload_multiple_with_own_title_image.html', {'form': form, 'message': message})
Output:
Multiple image uploads as per your requirements:
For uploading 'n' numbers of images without any maximum limits, you just need to use JavaScript for making dynamic forms. Here is what is below:
<!-- upload_multiple_image.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multiple Image Upload</title>
</head>
<body>
<h2>Upload Multiple Images with own title</h2>
<button onclick="add_more_image()">Add more</button>
{% if message %}
<p>{{ message }}</p>
{% endif %}
<form method="post" enctype="multipart/form-data" id="multi-image-form">
{% csrf_token %}
{{ form.as_p }}
<br>
{{ form.as_p }}
<br>
{{ form.as_p }}
<div id="dynamic-image-form">
</div>
<button type="submit">Upload</button>
</form>
<script>
var image_form = '{{ form.as_p|escapejs }}'
function add_more_image() {
console.log("flsdkfjsdlkf")
let form = document.getElementById("dynamic-image-form")
form.innerHTML += "<br>"
form.innerHTML += image_form;
}
</script>
</body>
</html>
add_more_image is a javascript function that makes the form dynamic. This function is called by a button click. It adds new fields in the form for taking new images with titles. By using this technique, you can upload images as per your requirements.
Output:
Multiple images related to one object:
It is a very common scenario for multiple image uploads. In this case, you need to upload multiple images, and those images are related to an object. For example, an e-commerce product has many preview images. For the product register, product name, description, price, etc., and many preview images are attached.
Models:
Here, one product has many images. There are two models required: Product and PreviewImage. PreviewImage model related to product by foreign key as one to many relationship.
class Product(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
class PreviewImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
image = models.ImageField(upload_to='preview_images/')
Form:
Create a new form named ProductForm. Here, there is no need to add handle images. Images will be handled directly from the template..
from django import forms
from .models import Product
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ['name', 'description']
Template:
Create a new file named upload_product.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload Product</title>
</head>
<body>
<h2>Upload Product</h2>
{% if message %}
<p>{{ message }}</p>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ product_form.as_p }}
<input type="file" name="images" multiple>
<button type="submit">Upload</button>
</form>
</body>
</html>
<input type="file" name="images" multiple>
can take input from multiple images.
View:
from django.shortcuts import render, redirect
from .forms import ProductForm
from .models import PreviewImage
def upload_product_view(request):
message = None
if request.method == 'POST':
product_form = ProductForm(request.POST)
if product_form.is_valid():
product = product_form.save()
# Handling multiple images
for img in request.FILES.getlist('images'):
PreviewImage.objects.create(product=product, image=img)
message = 'Product uploaded successfully!'
else:
product_form = ProductForm()
return render(request, 'upload_product.html', {'product_form': product_form, 'message': message})
Configure URL:
# yourappname/urls.py
from django.urls import path
from .views import upload_product_view
urlpatterns = [
path('upload-product/', upload_product_view, name='upload_product'),
]
Output: