背景: 我有两个模型:SellingItem和SellingItemImages。 SellingItemImages具有可容纳多个文件的自定义FileField。通过将两个表单(itemform和imageform)放在单个元素(enctype =“ multipart / form-data”)下,我可以允许用户上传多个图像。现在,我想结合客户端图像优化和更好的UI。我尝试了Filepond,但面临一些挑战。我通过
整理了这篇文章** 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上完成的工作
使用上面的代码,我能够 一种。显示文件池拖放区域 b。显示图片预览 C。 filepond显示上传完成,如下图所示 d。在Chrome开发工具控制台中,显示“所有文件的处理完成”
image showing filepond drop area after selecting two files
4)有关下一步操作的问题
a:与服务器相关:我了解带有“上传完成”的绿色突出显示是针对用户的。这并不一定意味着文件已上传到服务器。
文件是否已上传到服务器? 是我的服务器配置。正确? 如何知道文件是否已上传(使用控制台)到服务器?
b:与django相关:将文件上传到服务器后,如何检索这些文件并指向正确的django模型(在我的情况下为SellingItemsImages)?
我尝试过{= {3}}所示的files = request.FILES.getlist('filepond'),但是文件返回了空列表。我不知道这是因为此代码段不起作用,还是因为我没有上载任何文件。
c:与django表格相关:如背景所述,我有两种形式,一种是带有名称,价格等的常规形式;另一个用于上传图像。没有filepond,我在一个post_newitem视图中发送了两个带有一个提交按钮的表单。使用filepond,我想我有两个选择: -选项1:使用“提交”按钮发送常规表单,而异步发送filepond文件。 -选项2:让文件缓冲(通过transformplugin)优化图像,并将图像和其他表单区域(名称,价格等)作为FormData发送。
我希望就这两个选项以及如何进行这两个选项取得正反方面的意见。
答案 0 :(得分:0)
首先,感谢您的详细问题。
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,您可以在用户离开并返回页面时恢复临时上传的文件。我在这里写过关于上传客户端编辑过的文件的利弊的信息:https://pqina.nl/blog/the-trouble-with-editing-and-uploading-files-in-the-browser/