Django项目中FilePond的使用方法

How to use FilePond in Django project

背景: 我有两个模型:SellingItem 和 SellingItemImages。 SellingItemImages 有一个自定义的 FileField,可以容纳多个文件。通过在单个元素 (enctype="multipart/form-data") 下放置两种形式(itemform 和 imageform),我能够允许用户上传多张图片。现在,我想合并客户端图像优化和更好的 UI。我尝试了 filepond,但面临一些挑战。我通过

组织了这个 post
  1. 显示没有 filepond 的 django 代码
  2. 显示 filepond 的代码
  3. 到目前为止,我已经完成了 filepond
  4. 关于下一步做什么的问题

** 1) 没有 filepond 的 django 代码。** models.py

# models.py
class SellingItem(models.Model):
    seller = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
    description = models.CharField(max_length= 500, null=True, blank=True)
    price = models.IntegerField(default=0)


class SellingItemImages(models.Model):
    sellingitem = models.ForeignKey(SellingItem, default = None, on_delete=models.CASCADE, related_name='images')
    image = ContentTypeRestrictedFileField(content_types=['image/png', 'image/jpeg','image/jpg'],blank=True, 
                                   max_upload_size=5242880) 
    #ContentTypeRestrictedFileField is a custom FileField. 

这里是forms.py

class SellingItemForm(forms.ModelForm):

    class Meta:
        model = SellingItem
        fields = ('name', 'description', 'price')

class SellingItemImagesForm(forms.ModelForm):

    class Meta:
        model = SellingItemImages
        fields= ('image',)
        widgets = {
            'image': forms.FileInput(attrs={'multiple':True,}),
        }

这里是views.py

@login_required
def post_newitem(request):

    if request.method == 'POST':

        itemform = SellingItemForm(request.POST)
        imageform = SellingItemImagesForm(request.POST, request.FILES)

        if '_cancel' in request.POST:
            itemform = SellingItemForm()
            imageform = SellingItemImagesForm()
            return render(request, 'market/post_newitem.html',
                  {'itemform': itemform, 'imageform': imageform})

        else:
            if '_publish' in request.POST:  
                print('hit publish')  
                if itemform.is_valid() and imageform.is_valid():
                    print('two forms are valid')
                    sellingitem = itemform.save(commit=False)
                    sellingitem.seller = request.user
                    sellingitem.published_date = timezone.now()
                    sellingitem.save()

                    files = request.FILES.getlist('image')
                    for f in files:
                        photo = SellingItemImages(sellingitem=sellingitem, image=f)
                        photo.save()
                    return redirect('market_home')    
                else:
                    print(itemform.errors, imageform.errors)    

    else:
        itemform = SellingItemForm()
        imageform = SellingItemImagesForm(request.POST)
    return render(request, 'market/post_newitem.html',
                  {'itemform': itemform, 'imageform': imageform})

这是模板post_newitem.html。这里我把两个表单放在单个元素下。

{% extends 'market/marketbase.html' %}
{% block content %}
    <form id="post_form" method="post" action="" enctype="multipart/form-data">

    {% csrf_token %}

    {% for hidden in itemform.hidden_fields %}
        {{ hidden }}
    {% endfor %}

    {% load widget_tweaks %}
        <table>
            <tr>
                <td>{{ itemform.name |add_class:"name_form_field"}}</td>
            </tr>
            <tr>
                <td>{{ itemform.description |add_class:"description_form_field" }}</td>
            </tr>
            <tr>
                <td>{{ itemform.price |add_class:"price_form_field" }}</td>
            </tr>

        {% for hidden in imageform.hidden_fields %}
            {{ hidden }}
        {% endfor %}
            <tr>
                <td>
                    {{ imageform.image |add_class:"image_form_field" }}
                </td>
            </tr>
        </table>

    <input class='edit-delete-buttons' type="submit" name="_publish">
    <input class='edit-delete-buttons' type="submit" name="_cancel">
    </form>
{% endblock %}

以上代码允许用户上传多张图片。如前所述,为了获得更好的 UI 和客户端图像优化,我求助于这个不错的 javascript 库 filepond。

2) filepond

代码
<script>
document.addEventListener('DOMContentLoaded', function() {

    // Create FilePond object
    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement, {
        // track addfile event
        onaddfile: (err, fileItem) => {
        console.log(err, fileItem.getMetadata('resize'));
        },
        // to see whether all files are processed
        onprocessfiles: () => {
            console.log('process finished for all files');
        },
        // show when a file is reverted
        onprocessfilerevert: (fileItem) => {
            console.log(fileItem + 'is reverted');
        },

    });
});

    FilePond.registerPlugin(
        FilePondPluginImagePreview,
        FilePondPluginImageCrop,
        FilePondPluginImageTransform,
        FilePondPluginFileValidateType,
        FilePondPluginImageResize);

    var csrf_token="{{ csrf_token }}";

    FilePond.setOptions({
        imagePreviewHeight: 100,
        allowMultiple: true,
        imageCropAspectRatio: 1,
        imageResizeTargetWidth: 256,
        imageResizeMode: 'contain',
        imageTransformOutputQuality: 80,
        maxFiles: 4,

        server: {
            // url, none, because endpoints located on the same server
            process: {
                headers: {"X-CSRFToken":csrf_token,},
                url: '/media/',
                method: 'POST',
            },
            revert: {
                headers: {
                "X-CSRFToken":csrf_token,
                },
                url: '/media/',
                method: 'DELETE',
            },
            fetch: null,
            load: null,
        }
        });
    </script>

3) 到目前为止,我用 filepond 完成了什么

通过上面的代码,我能够 一种。显示 filepond 拖放区域 b.显示图像预览 C。 filepond 显示上传完成,如下图所示 d.在 Chrome 开发工具控制台中,显示 "process finished for all files"

image showing filepond drop area after selecting two files

4) 关于下一步做什么的问题

a: server related:I 明白带有 "upload finished" 的绿色突出显示是给用户的。这并不一定意味着文件已上传到服务器。

文件上传到服务器了吗? 是我的服务器配置。正确的? 如何知道文件是否已上传到服务器(使用控制台)?

b:django 相关:文件上传到服务器后,如何检索这些文件并指向正确的 django 模型(在我的例子中是 SellingItemsImages)?

我尝试了 files=request.FILES.getlist('filepond'),如此 所示,但 files 返回空列表。我不知道这是因为这个片段不起作用,还是因为我没有上传任何文件开始。

c: django form related: 后台说了,我有两种形式,一种是常规形式,有名称,价格等;另一个用于上传图像。在没有 filepond 的情况下,我在一个 post_newitem 视图中用一个提交按钮发送了两个表单。对于 filepond,我想我有几个选择: - 选项 1:发送带有提交按钮的常规表单,同时异步发送 filepond 文件。 - 选项 2:让 filepond 优化图像(通过 transformplugin),并将图像和其他表单区域(名称、价格等)作为 FormData 发送。

我希望就这两个选项的优缺点以及如何继续这两个选项获得一些意见。

首先感谢您的详细提问。

a) 绿色表示服务器已对上传请求返回 200 OK 响应。所以应该上传文件。您可以通过单击 POST 请求并检查其主体(它应显示为二进制数据/多部分表单数据)来检查文件是否已在您的开发工具网络选项卡中发送。

b) 我对 Django 不熟悉,所以恐怕我无法帮助解决 Django 方面的问题。 FilePod 确实为每个 POST 发送了两个项目,一个 JSON 对象(文件的元数据)和一个文件对象,也许这会导致 Django 出现一些问题?更多相关信息:https://pqina.nl/filepond/docs/patterns/api/server/

还有这个可能有用的 Django 服务器组件:https://github.com/ImperialCollegeLondon/django-drf-filepond

c) 选项 2 的优点是不必存储临时文件,但最终表单提交将花费更长的时间,如果存在大文件,则在慢速/移动连接上可能不太稳定。使用选项 1,您可以在用户离开和 returns 到页面时恢复临时上传的文件。我在这里写了关于上传客户端编辑文件的 pros/cons:https://pqina.nl/blog/the-trouble-with-editing-and-uploading-files-in-the-browser/