Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nesting dropzone in existing form? #14

Open
clpatterson opened this issue Jun 26, 2018 · 24 comments
Open

Nesting dropzone in existing form? #14

clpatterson opened this issue Jun 26, 2018 · 24 comments

Comments

@clpatterson
Copy link

clpatterson commented Jun 26, 2018

I have a form for posting blog entries. I want to be able to submit multiple fields at once. Instead of designating the class of the form as 'dropzone' is it possible to be able to have a dropzone div nested in my current form so one button will submit all my fields, including multiple image upload, at once?
Something like this stackoverflow question, but with flask-dropzone. Thanx in advance for any assistance!

My form:

<!DOCTYPE HTML>
<form action="{{ url_for('add_post') }}" method="post" class="add-post" enctype="multipart/form-data">
   <dl>
      <dt>Post title:
      <dd><input type="text" size="30" name="title" spellcheck="true" required>
      <dt>Post date:
      <dd><input type="date" name="post_date" required>
      <dt>Post description(one sentence):
      <dd><textarea name="description" rows="3" cols="40" spellcheck="true" required></textarea>
      <dt>Post html file:
      <dd><input type="file" name="html_file" required>
      <dt>Post image(s) file:</dt>

      <!--Can something like this be done?-->
      <div class="dropzone" id="myDropzone">
         {{ dropzone.create(action=url_for('show_posts')) }}
      </div>

      <dd><input type="submit" value="Submit">
   </dl>
</form>
@greyli
Copy link
Member

greyli commented Jun 27, 2018

Thanks, this is a very helpful feature, I will try to implement it this week.

@greyli
Copy link
Member

greyli commented Jun 30, 2018

It seems that there isn't a perfect way to handle this problem. As far as I know, the following thing is easy:

  • Insert dorpzone div inside a form
  • Use submit button to upload all dropped files

But the problem is, Dropzone.js use AJAX to send files, so we can't merge the files with the normal POST request that created when you click the submit button. The most of the methods in the SO question you posted is append the form data into the AJAX request that created by Dropzone.js, but then it will be hard to get the error messages from server-side when validation fails.

@greyli
Copy link
Member

greyli commented Jun 30, 2018

An alternative method is to send uploads and other form data to different view functions, for example:

  • Upload files --> /upload
  • Other fields --> /new

However, I can't make the form submit after file upload complete:

    document.getElementById("submit").addEventListener("click", function(e) {
         // prevent submit form first
        e.preventDefault();
        e.stopPropagation();
        myDropzone.processQueue();  // upload files
        document.getElementById("submit").click();  // submit other fields --> not work
    });

Any idea?

@greyli
Copy link
Member

greyli commented Jun 30, 2018

I finally make the alternative method worked, you can try the demo application in here.

@gyrcom
Copy link

gyrcom commented Jul 3, 2018

Trying the demo. Get errors
BuildError: Could not build url for endpoint ''. Did you mean 'handle_upload' instead?
It seems the url_for command is not getting the config setting for DROPZONE_UPLOAD_ACTION

@greyli
Copy link
Member

greyli commented Jul 4, 2018

@gyrcom Thanks for the feedback! It should be work with the code on master branch, you can install it on local with:

$ git clone https://github.com/greyli/flask-dropzone.git
$ cd flask-dropzone
$ pip install -e .

then run the demo with:

$ python examples/in-form/app.py

@gyrcom
Copy link

gyrcom commented Jul 4, 2018

Thank You

@gabrielggg
Copy link

gabrielggg commented Jul 5, 2018

i tried the code and received this output from server when submitting the form

Traceback (most recent call last):
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1997, in call
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1985, in wsgi_app
response = self.handle_exception(e)
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1540, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.5/dist-packages/flask/_compat.py", line 33, in reraise
raise value
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.5/dist-packages/flask/_compat.py", line 33, in reraise
raise value
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1598, in dispatch_request
return self.view_functionsrule.endpoint
File "/home/deeplearning/gabriel/front/front/test/flask-dropzone/examples/in-form/app.py", line 33, in handle_upload
for key, f in request.files.iteritems():
AttributeError: 'ImmutableMultiDict' object has no attribute 'iteritems'

wasnt able to upload files

i solved it putting items() instead of iteritems() in python3

@greyli
Copy link
Member

greyli commented Jul 6, 2018

@gabrielggg Yes, you are right, I forgot to add a comment to address this.

@Permafacture
Copy link

Both python 2 and 3 have a dict.items. There's really no memory or performance benefit to not just using items in both python 2 and 3.

@Permafacture
Copy link

Permafacture commented Aug 17, 2018

Hmm, sticky. Assuming we need the files and form data in one function in order to create a database record or something, I can think of three options:

  1. Don't use AJAX for the dropzone component when it is part of a larger form, and lose the nice dropzone validation feedback. Instead embed the file upload validation feedback in the template like the rest of the form elements.

  2. Merge the form data into the AJAX, and allow the user to specify a callback function which get the response (after dropzone has used it) and can update the form elements.

  3. Do two requests as you are currently doing and include a common uuid in both requests. The file handling function can use the key to put the files somewhere where the form data handling function can retrieve them.

#1 clears the dropzone ui so server side per file validation messages can't be shown the way dropzone usually does. (is this a big deal?) . #2 could be fine but would compel users to figure out how to display field and form validation messages through javascript rather than through the templates like WTForms. This might even merit a library of it's own, since adding other filed types is probably out of scope for flask-dropzone. #3 is trivial after the work that has already been done. It is limited in that form validation cannot influence file validation but I can't really think of a solid example of this being an issue (clearing the dropzone files in the UI on form level validation failure seems fine in all situations I can think of).

@greyli
Copy link
Member

greyli commented Aug 18, 2018

Both python 2 and 3 have a dict.items. There's really no memory or performance benefit to not just using items in both python 2 and 3.

Technically, use dict.iteritems() did can improve performance since it did not create a new list. However, use dict.items() in an example application is enough and it will be more friendly for users use different Python version. I will update the example application and README later this day (You can also make a PR if you want).

As for nesting form issue, thanks for your thoughful analysis. I actually trid to implement #1 and #2, but both are diffcult to implement or hard to use. #3 looks good, I'm happy to review the PR if you can implement it.

@greyli
Copy link
Member

greyli commented Aug 18, 2018

items() issue fixed in f30a61d.

@Permafacture
Copy link

I've created a pull request addressing #3 and partially #2. #3 really just needed the example updated, using the csrf token, though if you are fine with using a real CSRF token in that example then I'd add hashing the token rather than using the token to make the path directly. Completing #2 would require an example of updating the init function with a callback. I might do that if we end up needing that functionality for our project.

ps. not sure how link the the PR. haven't done this on github before

pps. iteritems doesn't make a list but it does make a generator object, so it's still constructing a new python object. It's not free. items builds the list in C code which is really fast. Especially for dictionaries with only dozens of elements, it's not clear to me which would be faster, and both would be so fast that doing anything with each element would dwarf the time it takes to make the list or generator. And network io would be orders of magnitude more. So, I disagree that iteritems improves performance here.

@greyli
Copy link
Member

greyli commented Aug 19, 2018

#2 is not a good idea since user will need a normal POST request to handle form validation then render the page with error messages.

You can just type # with the number to link an issue or PR.

I agree, it is almost no different for small dict.

@harry-wright
Copy link

My take was similiar to @Permafacture

I was considering generating a random UUID.

The images would upload instantly to a '/temp/string:UUID' folder using handle_images_uploads().

The form would submit with UUID to handle_form() and check if there were any images added to the folder.

If my understanding's correct though - I don't seem to be able to send the CSRF token twice. So it adds vulnerability to the app.

@harry-wright
Copy link

I'm also not sure if anyone's seen this
link

@greyli
Copy link
Member

greyli commented Sep 12, 2018

@harry-wright The current implementation was just inspired by the link you posted.

@gyrcom
Copy link

gyrcom commented Jan 21, 2019

Back to this again, the examples work very well, I am trying however, to get in-form working with csrf.
If not possible, I will just implement a popup. Thx

@gyrcom
Copy link

gyrcom commented Jan 25, 2019

I've spent a fair lot of time trying lots of options. In-form works well until I try to get csrf working. I do not have the necessary javascript skills to debug this. Has anyone gotten in-form and csrf working together?

@gyrcom
Copy link

gyrcom commented Jun 10, 2019

Well I've spent some more time on this. The problem stems from not being able to use the csrf token more than once. My work around has been to start a javascript popup window to implement the dropzone, and when finished, use queuecomplete to then close the popup. I do not think there is any easy answer to integrating dropzone, forms, and csrf.

@harry-wright
Copy link

harry-wright commented Jun 10, 2019 via email

@greyli
Copy link
Member

greyli commented Jun 12, 2019

@harry-wright Thanks for the tip, I'll have a look at this when I have free time.

@itsDrac
Copy link

itsDrac commented Dec 15, 2020

Hi @greyli I've been looking for the answer for this issue but i can not find any thing that would be of any help.
Besides that, your commit from 7 day ago does seem to provide an alternative solution
However it would be great conveniences if this issue get resolves...

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants