【问题标题】:Why images are not submitted when using ajax call in django?为什么在 django 中使用 ajax 调用时没有提交图像?
【发布时间】:2022-01-12 19:45:38
【问题描述】:

我正在使用 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": ""})
    }

同样,imagefield 与 post 模型具有多对多关系。 我做错了什么?

这是我用清晰的表单渲染表单本身的模态

        <!-- create post modal -->
    <div class="modal fade" id="CreatePostModal" data-url="{% url 'post-list-view' %}" tabindex="-1">
        <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Creating Post</h5>
                    <button type="button" class="close" data-bs-dismiss="modal" aria-label="Close">
                        &times;
                    </button> 
            </div>       
            <div class="modal-body">
                <div class="container-fluid">
                   {% crispy form %}
                   <div id="PreviewImagesContainer">
                       
                   </div>
                </div>
            </div>
            <div class="modal-footer float-right">
                <button type="button" class="btn btn-sm btn-dark" data-bs-dismiss="modal">Cancel</button>
                <button type="submit" form="createPostForm" id="createPostBtn" class="btn btn-sm btn-primary">Post</button>
                <span class="loading-icon"><i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
            </div>
            
        </div>
        </div>
    </div>
    <!-- end of create post modal -->

【问题讨论】:

  • 可以分享模板中的代码吗?
  • @dacx 我在渲染表单的地方添加了模式。
  • 如果它包含文件,通常这个 [enctype="multipart/form-data" ] 用于在表单中发布

标签: django ajax django-views django-forms


【解决方案1】:

我最终使用 javascript 的 FormData 解决了这个问题,方法是遍历我从输入中获取的文件,然后将其附加到 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)
        }
    })
});

最后从views.py文件中的request.FILES获取图片。

    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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-05
    • 2021-09-30
    • 2019-02-04
    • 1970-01-01
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    相关资源
    最近更新 更多