jeanphix / django-resumable

django backend for resumable.js xhr uploads.
61 stars 21 forks source link

"This field is required" anytime I try to upload a file #9

Closed staticfloat closed 7 years ago

staticfloat commented 7 years ago

Hello there,

I'm trying to use this in a new Django app I'm writing, and I'm having a hard time wrapping my head around how the ResumableFileField fits in with all my other fields. I have a Form that takes in many things, like user id, a note about the file, and the file itself:

class RecordingInfoForm(forms.Form):
    user_id = forms.CharField(label='Patient id', max_length=100)
    note_text = forms.CharField(label='Note', max_length=1000)
    file = ResumableFileField(
        allowed_mimes=("audio/flac",),
        upload_url=lambda: reverse('upload'),
        chunks_dir=getattr(settings, 'FILE_UPLOAD_TEMP_DIR')
    )

This is what my template looks like:

<form method="post" action=".">
    <fieldset>
        {% csrf_token %}
        {{ form.as_p }}
    </fieldset>
    <p><input type="submit" value="send" /></p>
</form>

This is what my views look like:

# mapped in `urls.py` to `/addInfo`
@login_required
def addInfo(request):
    cur_fullname = request.user.get_full_name()
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = RecordingInfoForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
           # ... Load form data into our models
    # if a GET (or any other method) we'll create a blank form
    else:
        form = RecordingInfoForm()
    context = {
        'form': form,
        'site_full_name' : cur_fullname,
    }
    return render(request, 'app/addInfo.html', context)

# Mapped in `urls.py` to `/upload`
class MyResumableUploadView(ResumableUploadView):
    @property
    def chunks_dir(self):
        dirname = self.request.user.username
        try:
            os.mkdir(os.path.join('/app/files', dirname))
        except:
            pass
        return os.path.join('/app/files', dirname)

This Form is supposed to collect the data when the user hits "submit", but I've noticed that the files do not upload, and we get a This field is required error whenever we do so. The Django view that is both rendering our template and receiving all the form fields is different than the upload endpoint that the ResumableFileField is pointed to by the upload_url parameter. The upload_url parameter points to /upload, and looking in my browser's inspector tool, I see that /upload is never accessed.

Is it the case that the file only starts uploading after I hit the submit button when using django-resumable in this manner? If so, how does the browser know to use resumable.js at all? If not, do you have any idea what I'm doing wrong so that the file is not being uploaded after being chosen in the file picker dialog? It's almost like resumable.js is not being used at all by my browser.

hendi commented 7 years ago

You're probably using Python 3, which removed Dict.iteritems. This is however used in the widget's template in order to set the data-upload-url, and with that attribute missing the resumable.js uploader is not initialized for your field, thus nothing works.

You can either change the template to use items instead of iteritems, or use my fork

Clara-Lu commented 7 years ago

Thanks Hendi for getting back to us! Your fork was able to fix part of the issue, and now the file is getting uploaded once we choose a file. However, when we try to "submit" after it is done uploading, we are getting this error message:

resumableissue

We tried setting the field (ResumableFileField) as not required (required=False)in the form, but then django gave us an error since it was not able to verify the form. It seems like resumable and the django form is working separately such that django form is not able to get the file uploaded with resumable. Do you have any insights about what is causing this? Thanks again!

Clara-Lu commented 7 years ago

Hi Hendrik,

Thanks for getting back to me! Your proposed hackish solution:

Hi Clara-Lu, the error message comes from the browser because the field has required set. You could either remove the required once the file is done uploading, or remove the file input completely if you want the user to only upload exactly one file.

One (hackish) way to achieve that would be to include the following code in your template:


   var parentOnFileSuccess = DjangoResumable.prototpe.onFileSuccess;
   DjangoResumable.prototype.onFileSuccess = function(r, file, message, el, progress, filePath, fileName) {
       parentOnFileSuccess(r, file, message, el, progress, filePath, fileName);
       // remove the file upload field
       document.getElementsByName("file")[0].remove();
   };
</script>

is able to solve my previous issue that django-end wasn't able to get the file and therefore couldn't validate the form. However, we ran into this new issue -

resumableissue_flac

We discovered that this is caused by this property allowed_mimes=("audio/flac"), we defined in ResumableFileField in the form. If we remove that property, the form works. It looks like django-resumable is not supporting that property properly. Can you take a look? Thanks again!

hendi commented 7 years ago

Hi Clara-Lu,

the content_type property does exist in Django's UploadedFile class. The error in your case is that Resumable received None instead of an UploadedFile. This could have several reasons, so you'll need to dig a bit.

For starters, have a look in your upload directory if the complete file is in there, or just the chunks. I assume it's just the chunks, and that means that the concatenation didn't work for you. In order to debug that you could edit django-resumable's file.py file. There's a method in there which handles the assembly from chunks to complete file. Maybe add some debug prints there to find out what's going wrong. (Sorry, can't be more specific right now as I'm on the road)

staticfloat commented 7 years ago

Clara and I are working together, so I can address some of the questions here:

The error in your case is that Resumable received None instead of an UploadedFile

Yes, I believe this is the root cause of all our trouble, including the original issue which we worked around with by removing the file field by using the javascript snippet suggested above. It looks like when we select a file with the file picker UI, the file is uploaded properly through the Resumable.js endpoint, but there is no record of the file in the "normal" HTML File object, so when we finally submit the whole form, the form contains no mention of the file. It's as if the Resumable.js and normal Django form logic are completely separate.

For starters, have a look in your upload directory if the complete file is in there, or just the chunks.

We get the full assembled file, and have manually verified that all the data is there by hashing the contents.

staticfloat commented 7 years ago

bump

Clara-Lu commented 7 years ago

bump

jeanphix commented 7 years ago

@Clara-Lu @staticfloat ^ should fix