Home > Blockchain >  Why images are not submitted when using ajax call in django?
Why images are not submitted when using ajax call in django?

Time:01-15

I am using a modelForm to create post objects via ajax. The images field are part of the form but not passed to the fields of the Meta class because that will allow to save the post first and add the images uploaded after that. My issue is if I do use a regular view(without ajax) the request.FILES are being submitted correctly but when I use via ajax those files are not part of the request.files which renders an empty <MultiValueDict: {}> I don't really know why. Here is my code.

    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)
        }
    })
});

Here is the form file

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": ""})
    }

again the imagefield has a manytomany relationship with the post model. What I am doing wrong?

Here is the modal where I am rendering the form itself with crispy form

        <!-- create post modal -->
    <div  id="CreatePostModal" data-url="{% url 'post-list-view' %}" tabindex="-1">
        <div >
        <div >
            <div >
                <h5 >Creating Post</h5>
                    <button type="button"  data-bs-dismiss="modal" aria-label="Close">
                        &times;
                    </button> 
            </div>       
            <div >
                <div >
                   {% crispy form %}
                   <div id="PreviewImagesContainer">
                       
                   </div>
                </div>
            </div>
            <div >
                <button type="button"  data-bs-dismiss="modal">Cancel</button>
                <button type="submit" form="createPostForm" id="createPostBtn" >Post</button>
                <span ><i  aria-hidden="true"></i>
            </div>
            
        </div>
        </div>
    </div>
    <!-- end of create post modal -->

CodePudding user response:

I finally solved it using the FormData of javascript by looping through of the files that I am getting from the input then append it to the data of the 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)
    }

After getting and resizing all the images I made the ajax call like that:

    $("#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)
        }
    })
});

and finally getting the images from request.FILES in the views.py file.

    def post(self, request, *args, **kwargs):
    form = PostForm(request.POST or None, request.FILES or None)
    result = {}
    files = request.FILES.getlist("images")
    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)
  •  Tags:  
  • Related