Python/Flask – after upload via dropzone, is does not wait for submit button

Question:

I’m creating a Flask app in Python, and I struggle with the following:

I have 2 pages:

upload.html –> first uploading files, and if the user is happy, you can click submit and a waiting screen appears

{% block header %}

<head>
<!-- standard stuff -->
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.png') }}">

<!--<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.2/basic.css"> -->

</head>

<div class="header-with-image" style="width: 100%; background-color:white; height: 70px; color: white;padding:0px;">
      <img class='banner-image' src="{{ url_for('static', filename='images/logo_header.jpg') }}" style="display: block;
      margin-left: auto;
      margin-right: auto;
      width: 8%;
      margin-bottom:<y>px;">
</div>
<div class="row justify-content-center align-items-center">
    <h1>Title Tool</h1>
</div>
<div class='row head valign-wrapper' >
    <div class='col s8 rellax center-align' data-rellax-speed="-2">
        <br>
        <br>
    </div>
</div>

{% endblock %}


{% block content %}
<div >
    <h4 > Upload here </h4>
    <br>
<!--    <form action="/upload-file" method="POST" enctype="multipart/form-data" type="post">-->
    <head>
        <meta charset="UTF-8">
        <title>Flask-Dropzone Demo</title>
        {{ dropzone.load_css() }}
        {{ dropzone.style()  }}
    </head>
    <body>
        {{ dropzone.create(action='/upload_file') }}
        {{ dropzone.load_js() }}
        {{ dropzone.config() }}
    </body>
<!--    </form>-->
      <form action="/waiting_screen" method="POST" enctype="multipart/form-data">
        <div class="form-group">
            <input type = "submit" value = "Go" >
        </div>
      </form>
</div>

{% endblock %}

waiting_screen.html –> of course, this is the page with a small waiting animation

{% block header %}

<head>
<!-- standard stuff -->
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.png') }}">

<style>

  #loader {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 1;
  width: 150px;
  height: 150px;
  margin: -75px 0 0 -75px;
  border: 16px solid #f3f3f3;
  border-radius: 50%;
  border-top: 16px solid #3498db;
  width: 120px;
  height: 120px;
  -webkit-animation: spin 2s linear infinite;
  animation: spin 2s linear infinite;
  }

  @-webkit-keyframes spin {
  0% { -webkit-transform: rotate(0deg); }
  100% { -webkit-transform: rotate(360deg); }
  }
  @keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
  }
  /* Add animation to "page content" */
  .animate-bottom {
  position: relative;
  -webkit-animation-name: animatebottom;
  -webkit-animation-duration: 1s;
  animation-name: animatebottom;
  animation-duration: 1s
  }
  @-webkit-keyframes animatebottom {
  from { bottom:-100px; opacity:0 }
  to { bottom:0px; opacity:1 }
  }
  @keyframes animatebottom {
  from{ bottom:-100px; opacity:0 }
  to{ bottom:0; opacity:1 }
  }
  #myDiv {
  display: none;
  text-align: center;
  }
</style>

<!--<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.2/basic.css"> -->



</head>


<div class="header-with-image" style="width: 100%; background-color:white; height: 70px; color: white;padding:0px;">
      <img class='banner-image' src="{{ url_for('static', filename='images/logo_header.jpg') }}" style="display: block;
      margin-left: auto;
      margin-right: auto;
      width: 8%;
      margin-bottom:<y>px;">
</div>
<div class="row justify-content-center align-items-center" style="width: 100%;
    box-shadow: 0 4px 2px -2px rgba(0,0,0,.2);
    height:100px;
    background-color:#2c3350;
    margin-top:<x>px;
    text-align: center;">
    <h1>Consolidatie Tool</h1>
</div>
<div class='row head valign-wrapper' >
    <div class='col s8 rellax center-align' data-rellax-speed="-2">
        <br>
        <br>
    </div>
</div>

{% endblock %}


{% block content %}
<div action="/first-page-info-check" method="POST">
    <h4 style="text-align:center"> Bezig om documenten te verwerken... </h4>
    <br>
    <head>
        <div id="loader"></div>
        <div style="display:none;" id="myDiv" class="animate-bottom" action="/first-page-info-check">
            <h2>Tada!</h2>
            <p>Some text in my newly loaded page..</p>
        </div >
        <script>
        var myVar;
        function myFunction() {
        myVar = setTimeout(showPage, 3000);
        }

        function showPage() {
        document.getElementById("loader").style.display = "none";
        document.getElementById("myDiv").style.display = "block";
        }
        </script>
    </head>

</div>
{% endblock %}

And my python code is:

filename = None

def allowed_file(filename):
    if not "." in filename:
        return False
    ext = filename.rsplit(".", 1)[1]
    if ext.upper() in app.config["ALLOWED_FILE_EXTENSIONS"]:
        return True
    else:
        return False

@app.route("/upload-file", methods=["POST"])
def upload_file():
    global filename
    file = None
    if request.method == "POST":
        if request.files:
            file = request.files["file"]
            print("test2")
            if file.filename == "":
                print("No filename")
                return redirect("upload.html")
            if allowed_file(file.filename):
                filename = secure_filename(file.filename)
                file.save(os.path.join(app.config['UPLOADED_PATH'], filename))
                print("file uploaded")
                return redirect("upload.html")
            else:
                print("That file extension is not allowed")
                return redirect("upload.html")
    else:
        print("test3")
        return render_template("upload.html")

@app.route('/waiting_screen', methods=['POST'])
def waiting_screen():
    global filename
    return render_template("waiting_screen.html", file_name=filename)

so I would like the following to happen:

user puts files in the dropzone > is he/she is happy: click the "submit" button > then a waiting screen appears.

The problem is: after the files have been dropped in the dropzone, the app doesn’t wait for the submit button but goes directly to the next page. I think it has to do with {{ dropzone.create(action='/upload_file') }} but I’m not sure… when I remove it, it doesn’t show the dropzone anymore…

Does somebody have experience with this and could help me? Thank you so much 🙂


EDITED CODE after suggested:

I now have added the autoProcessQueue to the dropzone.config(), but still it doesn’t wait untill I click the button.. :

<div style="padding: 2%;font-family: Verdana; margin:15px; text-align: center;margin-left: auto;height: 40%;
margin-right: auto;background: #ffffff;border-radius:4px;border-color:red;
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);width:30%;" >
    <h4 style="text-align:center"> Upload hier je documenten voor de vergunning <br><br> die je wil consolideren </h4>
    <br>
    <head>
        <meta charset="UTF-8">
        {{ dropzone.load_css() }}
        {{ dropzone.style('border: 2px dashed #4e70ab; margin: 2%; min-height: 100px;')  }}
        {{ dropzone.create(action='upload_file') }}
        {{ dropzone.load_js() }}
        {{ dropzone.config(custom_init='dz = this;document.getElementById("upload-btn").addEventListener("click", function handler(e) {dz.processQueue();});',
                 custom_options='autoProcessQueue: false, addRemoveLinks: true, parallelUploads: 20,') }}
    </head>
      <form action="/waiting_screen" method="POST" enctype="multipart/form-data">
        <div class="form-group">
            <input style="font-family: Verdana; margin:15px; color:white; border:none;
                            border-radius:4px; font-size: 16px; padding:20px; background:#FFFFFF;
                            background-color: #4e70ab; cursor:pointer" id="upload-btn" type = "submit" value = "bevestig" >
        </div>
      </form>
</div>
Asked By: user2133561

||

Answers:

i think you should initialize your dropzone with the following option to disable initial processing:

autoProcessQueue: false

Answered By: Georgios Mpizas

You need to set Dropzone.js’s autoProcessingQueue config parameter to false and manually call processQueue() when the user clicks the submit button.

You can do this by using Flask-Dropzone’s Custom Configuration String functionality.

The docs mention your exact use case: you need to call dropzone.config in your template as

{{ dropzone.config(custom_init='dz = this;document.getElementById("upload-btn").addEventListener("click", function handler(e) {dz.processQueue();});',
                 custom_options='autoProcessQueue: false, addRemoveLinks: true, parallelUploads: 20,') }}

Replace upload-btn by your submit button’s ID to make it work.

Answered By: Simeon Nedkov

For those using the pure JavaScript method (as opposed to Flask-Dropzone), the autoProcessQueue option can be set to false to achieve the desired objective, however, it’s essential that your Dropzone init be done in the <head> – if you do it after the page loads (e.g. by placing it at the bottom of the page or using $(document).ready() it’s too late, and these options will have no effect (but no error message will surface to tell you that).

Here’s a worked through example. Given a dropzone that looks like this:

<form action="{{ url_for('pfcontent.uploadFileTemp')}}" class="dropzone dz-clickable" id="my-dropzone" method="POST">

and any number of additional normal input fields, and then a submit button inside that form that looks like this:

<button type="submit">Process File(s)</button>

and a script in the <head> that looks like this:

  <script type="text/javascript">
    Dropzone.options.myDropzone = {
        autoProcessQueue: false,
        uploadMultiple: true,
        parallelUploads: 10, 
        maxFiles: 10, 
        init: function() { 
            myDropzone = this;
            this.element.querySelector("button[type=submit]").addEventListener("click", function(e) {  
            e.preventDefault();
            e.stopPropagation();
            myDropzone.processQueue();
        });
        myDropzone.on("queuecomplete", function(file) {
           Called when all files in the queue finish uploading.
          alert("Completed");
            });
          }
    };
    </script>

we can get the desired behaviour.

Additionally, in my case I needed a second button that would enable the user to indicate that they had finished all the uploads. It’s kind of the flip side of the first problem – having successfully stopped DropZone from autoprocessing, now we need to be able to move on with our lives.

This button is placed outside of the DropZone form:

<button class="btn btn-outline-success btn-block" type="button" onclick="window.location.href='{{ url_for('pfcontent.uploadedFileTemp')}}';">Processing Complete</button>

where pfcontent.uploadedFileTemp is the blueprinted route function I want to call to move on (in this case, it’s a page of ‘most recent uploads’, which should show the user what they just uploaded)

I really like how DropZone with Flask performs, but figuring out how to make it work in the context of a form with other fields in it was a bit of an ordeal, so posting here in case someone finds this pattern useful.

Answered By: James_SO
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.