Search code examples
djangouploadmultifile-uploader

Django File upload looping


How would I best loop a form like this (there is other code in the form but this is an example for image uploading)

<form action="{% url "upload" %}" method="post" enctype="multipart/form-data">
  {% csrf_token %}
  <input type="file" name="image_file">
  <input type="file" name="image_file2">
  <input type="file" name="image_file3">
  <input type="file" name="image_file4">
  <input type="file" name="image_file5">
  <input type="submit" value="submit" />
</form>

for single file uploading but need it multifile uploading:

def image_upload(request):
    if request.method == "POST" and request.FILES["image_file"]:
        image_file = request.FILES["image_file"]
        fs = FileSystemStorage()
        filename = fs.save(image_file.name, image_file)
        image_url = fs.url(filename)
        print(image_url)
        return render(request, "upload.html", {
            "image_url": image_url
        })
    return render(request, "upload.html")

Just not sure how to loop it so images are stored along with filename in a var for the db (at a later point) also just a thought, might not always be uploading all five files could be three or none


Solution

  • If I am understanding you correctly, you are simply looking to loop through the 5 fields, image_file through image_file5, and save each one if it has a file uploaded?

    If so, that is almost trivially easy, since you can use the result of a calculation, or a variable, instead of just a hard-coded string. The only (slight) hiccup is that the first field isn't named with a "1", but that's easily handled with an if condition. I'll keep this simple for the sake of understanding; I would probably have taken a totally different approach if I was creating this functionality. Anyway, use a for loop, looping through the range 1 to 5 (range function returns an iterator from the first argument up to the second argument minus 1, which is why we're using range(1, 6) and not Range(1, 5)), and using the current iterator number to construct the name of the current file field. Like so:

    def image_upload(request):
        if request.method == "POST":
            image_urls = dict()
            for i in range(1, 6):
                file_key = f"image_file{i}" if i > 1 else "image_file"  # If you're using Python 2, f-strings won't work, but you could do either "image_file{}".format(i) or "image_file%d" % (i, )
                image_file = request.FILES.get(file_key, None)  # This format retrieves the file if the named file exists, otherwise it will result in None (because I specified None as the default falue in the second argument).  This is safer against potential errors, if the field doesn't exist or has no file associated
                if image_file:  # Since we stored the image file, or None if missing, in the variable called image_file, we'll just check that; if it has a non-Non value, this line resolves to True
                    fs = FileSystemStorage()
                    filename = fs.save(image_file.name, image_file)
                    image_urls[file_key] = fs.url(filename)
                else:
                    image_urls[file_key] = ""  # Or maybe you'd want None here
    
            #
            # Here I am rendering the template after saving all 5 files, and including the URLs for all 5.  But I don't know what the intent 
            # is here, so you might need to write something else.  In the context for this render call, I am unpoacking the dictionary key/value
            # pairs recorded in the loop, into keyword variable pairs.
            return render(request, "upload.html", {
                **image_urls,
            })
                
    

    I know this response is 2 years late, burt I came across this while searching for something, and I can't believe it's unresolved.