I have a Django project that uses Dropzone to upload multiple files to a project a user has created. All of this is within the same instance (project is created alongside file uploads), so I am using autoProcessQueue: false
and running dropzone.processQueue()
once the rest of the form data has been added.
I have a malformatted javascript file that ends up working. Files are posted and I can access them through <MultiValueDict: {'file': [<InMemoryUploadedFile: <img_name>.jpg (image/jpeg)>]}>
. This 'works' but I cannot upload multiple files.
My correctly formatted javascript doesn't return anything. When I drop a file into the dropzone space in the html, the preview shows but now file is loaded. When I POST
the form, I get this in the output for dropzone: <MultiValueDict:[]
Why is my bad JS partially functional, but my proper JS is not capturing any file uploads?
BAD but Working javascript.js for 1 file in dropzone
dropzonePreviewNode && (dropzonePreviewNode.id = "", previewTemplate = dropzonePreviewNode.parentNode.innerHTML, dropzonePreviewNode.parentNode.removeChild(dropzonePreviewNode), dropzone = new Dropzone(".dropzone", {
headers: {
'X-CSRFToken': csrftoken
},
autoProcessQueue: false,
url: "create",
method: "post",
uploadMultiple: true,
previewTemplate: previewTemplate,
previewsContainer: "#dropzone-preview",
acceptedFiles:'.jpg',
})
dropzone.on('success', dropzone.processQueue.bind(dropzone));
Correct JS, but not working - Causes Broken Pipe
:
dropzonePreviewNode && (dropzonePreviewNode.id = "", previewTemplate = dropzonePreviewNode.parentNode.innerHTML, dropzonePreviewNode.parentNode.removeChild(dropzonePreviewNode), dropzone = new Dropzone(".dropzone", {
headers: {
'X-CSRFToken': csrftoken
},
autoProcessQueue: false,
url: "create",
method: "post",
uploadMultiple: true,
previewTemplate: previewTemplate,
previewsContainer: "#dropzone-preview",
acceptedFiles:'.jpg',
}));
dropzone.on('success', dropzone.processQueue.bind(dropzone));
html:
<form action="{% url 'apps:projects.create'%}" method="POST" enctype="multipart/form-data" class="dropzone">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
... (other form data input for project model) ...
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Attached files</h5>
</div>
<div class="card-body">
<div>
<p class="text-muted">Add Attached files here.</p>
<div class="dropzone">
<div id="create-project" class="fallback">
<input name="file" type="file" multiple="multiple">
</div>
<div class="dz-message needsclick">
<div class="mb-3">
<i class="display-4 text-muted ri-upload-cloud-2-fill"></i>
</div>
<h5>Drop files here or click to upload.</h5>
</div>
</div>
<ul class="list-unstyled mb-0" id="dropzone-preview">
<li class="mt-2" id="dropzone-preview-list">
<!-- This is used as the file preview template -->
<div class="border rounded">
<div class="d-flex p-2">
<div class="flex-shrink-0 me-3">
<div class="avatar-sm bg-light rounded">
<img src="#" alt="Project-Image" data-dz-thumbnail class="img-fluid rounded d-block" />
</div>
</div>
<div class="flex-grow-1">
<div class="pt-1">
<h5 class="fs-14 mb-1" data-dz-name> </h5>
<p class="fs-13 text-muted mb-0" data-dz-size></p>
<strong class="error text-danger" data-dz-errormessage></strong>
</div>
</div>
<div class="flex-shrink-0 ms-3">
<button data-dz-remove class="btn btn-sm btn-danger">Delete</button>
</div>
</div>
</div>
</li>
</ul>
<!-- end dropzon-preview -->
</div>
</div>
</div>
<!-- end card -->
<div class="text-end mb-4">
<button type="submit" class="btn btn-danger w-sm" onclick="location.href = '{% url 'apps:projects.list' %}';">Cancel</button>
<button type="submit" class="btn btn-success w-sm" id="add-btn">Create</button>
</div>
</div>
</form>
view.py (Very simply, I just want to print the request.FILES
at the moment to see if dropzone is working. Comfortable saving to the right location)
def apps_projects_create_view(request):
if request.method == "POST":
form = ProjectListAddForm(request.POST, request.FILES or None)
if form.is_valid():
form.save()
print(form.cleaned_data)
print(request.FILES)
models.py
class ProjectList(models.Model):
identifier = models.AutoField(primary_key=True)
logo = models.ImageField(upload_to=project_directory_path, blank=True, null=True)
name = models.CharField(max_length=50)
client = models.ForeignKey('ClientCompany', blank=True, null=True, on_delete=models.CASCADE)
description = RichTextField(default="Please add Project description... ", blank=True)
create_date = models.DateField(auto_now_add=True)
end_date = models.DateField(blank=True, null=True)
due_date = models.DateField(blank=True, null=True)
assignment = models.ManyToManyField(TechnicianUser)
status = models.CharField(max_length=50,choices=PROJECT_STATUS)
priority = models.CharField(max_length=10,choices=PRIORITY, null=True, blank=True)
file = models.FileField(upload_to=project_directory_path, blank=True, null=True)
tag = TaggableManager(blank=True)
This StackOverflow Answer by @jmur is what solved my issue:
<script>
Dropzone.autoDiscover = false;
new Dropzone("#create-post",{
clickable: ".dropzone",
url: "{% url 'create_post' request.user.slug %}",
previewsContainer: "#previewsContainer",
paramName: "images",
maxFiles: 10,
maxFilesize: 30,
acceptedFiles: '.png, .jpg, .jpeg',
uploadMultiple: true,
parallelUploads: 20,
autoProcessQueue: false,
init() {
var myDropzone = this;
this.element.querySelector("#submit-all").addEventListener("click", function(e){
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
this.on("success", function(file, response) {
window.location.href=JSON.parse(file.xhr.response).url
});
}
});
</script>
This is what needed to be changed / included in my script
init() {
var myDropzone = this;
this.element.querySelector("#submit-all").addEventListener("click", function(e){
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
this.on("success", function(file, response) {
window.location.href=JSON.parse(file.xhr.response).url
});
}