Почему изображения не передаются при использовании ajax вызова в django?
Я использую modelForm для создания объектов поста через ajax. Поле изображений является частью формы, но не передается в поля класса Meta, потому что это позволит сначала сохранить пост, а затем добавить загруженные изображения. Моя проблема в том, что если я использую обычный вид (без ajax), то request.FILES передаются правильно, но когда я использую ajax, то эти файлы не являются частью request.files, что приводит к пустому <MultiValueDict: {}>
Я не знаю почему.
Вот мой код.
def post(self, request, *args, **kwargs):
form = PostForm(request.POST or None, request.FILES or None)
result = {}
files = request.FILES
print(files)
if is_ajax(request=request) and form.is_valid():
print("the request is ajax and the form is valid")
title = form.cleaned_data.get("content", "")
print("Title ", title)
post_instance = form.save(commit=False)
post_instance.author = request.user
result['success'] = True
return JsonResponse(result)
$.ajax({
url: $("#CreatePostModal").attr("data-url"),
data:$("#CreatePostModal #createPostForm").serialize(),
method: "post",
dataType: "json",
success: (data) => {
if (data.success) {
setTimeout(() => {
$(e.target).next().fadeOut();
ResetForm('createPostForm', 'PreviewImagesContainer')
$("#CreatePostModal").modal('hide')
$(e.target.nextElementSibling).fadeOut()
alertUser("Post", "has been created successfully!")// alerting the user
}, 1000)
console.log(data.title)
} else {
$("#createPostForm").replaceWith(data.formErrors);
$("#PreviewImagesContainer").html("");
$("#CreatePostModal").find("form").attr("id", "createPostForm");
$(e.target.nextElementSibling).fadeOut()
};
$(e.target).prop("disabled", false);
},
error: (error) => {
console.log(error)
}
})
});
Вот файл формы
class PostForm(forms.ModelForm):
images = forms.ImageField(
required=False,
widget=forms.ClearableFileInput(attrs={
'multiple': True,
})
)
class Meta:
model = Post
fields = ("content",)
widgets = {
"content": forms.Textarea(attrs={"placeholder": "Tell us something today....", "rows": 5, "label": ""})
}
снова поле изображения имеет многотонные отношения с моделью поста. Что я делаю неправильно?
Я наконец решил эту проблему, используя FormData из javascript, перебирая файлы, которые я получаю из ввода, а затем добавляю их к данным FormData.
let imageFiles = []
$("#CreatePostModal").on('change', (e) => {
$(postImagesPreviewContainer).html("")
if ($(e.target).attr("id") !== "id_images") return;
var filenames = "";
for (let i = 0; i < e.target.files.length; i++) {
filenames += (i > 0 ? ", " : "") + e.target.files[i].name;
}
e.target.parentNode.querySelector('.custom-file-label').textContent = filenames;
//why is the this element returning the document and not the target itself
// check the length of the files to know what template to make
const files = e.target.files
const numberOfImages = files.length
let gridColumnSize;
if (numberOfImages > 5 | numberOfImages === 0) return;
var row = document.createElement("div")
row.setAttribute("class", "post-images")
for (file of files) {
const postImageChild = document.createElement("div");
postImageChild.setAttribute("class", "post-images__child_down")
const reader = new FileReader();
reader.onload = () => {
img = document.createElement("img")
img.setAttribute("src", reader.result)
img.onload = (e) => {
// here i will process on resizing the image
const canvas = document.createElement("canvas")
const max_width = 680
const scaleSize = max_width / e.target.width
canvas.width = max_width
canvas.height = e.target.height * scaleSize
var ctx = canvas.getContext("2d") // setting the context of the canvas
ctx.drawImage(e.target, 0, 0, canvas.width, canvas.height)
const encodedSource = ctx.canvas.toDataURL(e.target, 'image/png', 1)
const processedImg = document.createElement("img") // create a processed image and return it.
processedImg.src = encodedSource
$(postImageChild).append(processedImg)
imageFiles.push(processedImg)
}
}
$(row).prepend(postImageChild)
$(postImagesPreviewContainer).append(row);
reader.readAsDataURL(file)
}
После получения и изменения размеров всех изображений я сделал ajax вызов следующим образом:
$("#CreatePostModal").on("click", (e) => {
if ($(e.target).attr("id") !== "createPostBtn") return;
e.preventDefault();
e.target.setAttribute("disabled", true);
$(e.target.nextElementSibling).fadeIn()
var form = $("#createPostForm")[0]
var data = new FormData(form); // getting the form data
console.log("this is the data", data)
for (var i = 0; i < imageFiles.length; i++) { // appending images to data
data.append('images', imageFiles[i]);
};
$.ajax({
url: $("#CreatePostModal").attr("data-url"),
data: data, //$("#CreatePostModal #createPostForm").serialize(),
method: "post",
processData: false,
cache: false,
contentType: false,
dataType: "json",
success: (data) => {
if (data.success) {
setTimeout(() => {
$(e.target).next().fadeOut();
ResetForm('createPostForm', 'PreviewImagesContainer')
$("#CreatePostModal").modal('hide')
$(e.target.nextElementSibling).fadeOut()
alertUser("Post", "has been created successfully!")// alerting the user
}, 1000)
} else {
$("#createPostForm").replaceWith(data.formErrors);
$("#PreviewImagesContainer").html("");
$("#CreatePostModal").find("form").attr("id", "createPostForm");
$(e.target.nextElementSibling).fadeOut()
};
$(e.target).prop("disabled", false);
},
error: (error) => {
console.log(error)
}
})
});
и, наконец, получение изображений из request.FILES
в файле views.py.
def post(self, request, *args, **kwargs):
form = PostForm(request.POST or None, request.FILES or None)
result = {}
files = request.FILES
if is_ajax(request=request) and form.is_valid():
post_obj = form.save(commit=False)
post_obj.author = request.user
print(post_obj)
post_obj.save()
for file in files:
new_file = Files(image=file)
new_file.save()
post_obj.images.add(new_file)
post_obj.save()
result['success'] = True
return JsonResponse(result)