How to upload multiple images from a single "Choose Files" selector in django admin

Kush Raj Rimal picture Kush Raj Rimal · Apr 14, 2018 · Viewed 8.1k times · Source

I'm doing this little project simulating a clothing ordering system to brush up my django skills.

I have to add several pictures of a certain item(not a fixed number). So, using a fixed number of image fields in Item model is not ideal. So, I thought of another model consisting only of images connected to the model 'items' by a key.

Now I know that django has the inline option for adding model objects one by one as per requirement. But that seems a bit of a sucker as you have to open that dialog box and select the images one by one.

Is it possible that there be a selector via which we could choose multiple files at once and they be added to the model??

Answer

DonExo picture DonExo · Apr 14, 2018

Yes, there is a handy way. First declare an image class, something like:

class Image(models.Model):
    condo = models.ForeignKey(Condo, on_delete=models.CASCADE, related_name='images')
    image = models.FileField(upload_to="images/")
    uploaded_at = models.DateTimeField(auto_now_add=True)

In the form creation don't add a line for presenting a FileField

class ListingForm(forms.ModelForm):
    ...
    ...
    #photos = forms.FileField(required=False) #Don't do it.

But rather in the template, among the other lines, add this too:

<form action="" method="post" autocomplete="off" enctype="multipart/form-data">
    {% csrf_token %}
    {{ your_form|crispy }} <br/>
    <input type="file" name="images" multiple>
    <input type="submit" value="Submit">
</form>

In the view that handles your POST request, you can process the uploaded images with:

from app.forms import Image


if request.method == 'POST':
...
...
    for file in request.FILES.getlist('images'):
        instance = Image(
            condo=Condo.objects.get(your_parent_objects_id),
            image=file
        )
        instance.save()

This creates new Image object for each image and saves the actual files to your media folder.

Additionally, I'd suggest to rename the files your users are uploading due to possible malicious acts.

I've done this by creating a small function and using it instead of the "images/" part for it.

import uuid
def images_directory_path(instance, filename):
    return '/'.join(['images', str(instance.condo_id.id), str(uuid.uuid4().hex + ".png")])

# in Image class (models.py)
image = models.FileField(upload_to=images_directory_path)

This last modification generates new names for the images as uuid4, adds .png extension to them and creates full link to the media/ folder.

enter image description here

Of course, there is no validation present whether the format or size of the uploaded files are valid, but that is a topic for another question.

You can access the images with the variable images as it is the related_name for all connected images to the given object (in this case Condo).

Lastly, this is a a great article by Vitor Freitas about using Javascript plugins for uploading multiple images with progress bar.