【发布时间】:2019-08-19 23:35:39
【问题描述】:
简介:我有一个 python Django 网络应用程序,允许用户创建帖子。每个帖子都有 1 张主图片,然后是与该帖子相关的额外图片(最多 12 张和最少 2 张)。我想让用户总共添加 13 张图像。 1 个主图像和 12 个额外图像。
问题:通常用户使用智能手机拍照。这使得图像大小高达 10MB 。 13张图片可以变成130MB的形式。我的 django 服务器最多可以接受 10MB 的表单。所以我不能减少图像服务器端
我想要做什么:我想要这样当用户将每个图像上传到表单时。该图像的大小在客户端减小,并使用 Ajax 异步保存在我的服务器上的临时位置。创建帖子后,所有这些图像都链接到帖子。所以基本上当用户在帖子创建表单上点击提交时。它是一种没有图像的超轻形式。听起来太雄心勃勃了..哈哈也许
我目前所拥有的:
- 我有没有异步部分的模型/视图(所有创建帖子的 django 部分)。如,如果添加所有图像后的表单小于 10MB。我的帖子是用多少张额外的图片创建的
- 我的 Javascript 代码可以减小客户端图像的大小并将其异步添加到我的服务器。我需要做的就是给它一个端点,这是一个简单的 url
- 我对我计划如何实现这一目标有一个粗略的了解
现在向您展示我的代码
我的模型(只是 django 部分,还没有添加异步部分)
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts')
title = models.CharField(max_length=250, unique=True)
slug = models.SlugField(allow_unicode=True, unique=True, max_length=500)
message = models.TextField()
post_image = models.ImageField()
class Extra (models.Model): #(Images)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='post_extra')
image = models.ImageField(upload_to='images/', blank=True, null=True, default='')
image_title = models.CharField(max_length=100, default='')
image_description = models.CharField(max_length=250, default='')
sequence = models.SmallIntegerField(validators=[MaxValueValidator(12), MinValueValidator(1)])
我的观点(只是 django 部分,还没有添加异步部分)
@login_required
def post_create(request):
ImageFormSet = modelformset_factory(Extra, fields=('image', 'image_title', 'image_description'), extra=12, max_num=12,
min_num=2)
if request.method == "POST":
form = PostForm(request.POST or None, request.FILES or None)
formset = ImageFormSet(request.POST or None, request.FILES or None)
if form.is_valid() and formset.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
for index, f in enumerate(formset.cleaned_data):
try:
photo = Extra(sequence=index+1, post=instance, image=f['image'],
image_title=f['image_title'], image_description=f['image_description'])
photo.save()
except Exception as e:
break
return redirect('posts:single', username=instance.user.username, slug=instance.slug)
现在为了简单起见,我不会在这个问题中添加任何 Javascript。将以下脚本标记添加到我的表单会使图像异步保存到服务器。如果您愿意,可以阅读更多关于Filepond 的信息
'''See the urls below to see where the **new_image** is coming from'''
FilePond.setOptions({ server: "new_image/",
headers: {"X-CSRF-Token": "{% csrf_token %}"}}
}); #I need to figure how to pass the csrf to this request Currently this is throwing error
我的计划
在现有的 2 个模型下面添加一个新模型
class ReducedImages(models.Model):
image = models.ImageField()
post = models.ForeignKey(Post, blank=True, null=True, upload_to='reduced_post_images/')
如下更改视图(目前仅在主图像上工作。不确定如何获取额外图像)
''' This could be my asynchronous code '''
@login_required
def post_image_create(request, post):
image = ReducedImages.objects.create(image=request.FILES)
image.save()
if post:
post.post_image = image
@login_required
def post_create(request):
ImageFormSet = modelformset_factory(Extra, fields=('image', 'image_title', 'image_description'), extra=12, max_num=12,
min_num=2)
if request.method == "POST":
form = PostForm(request.POST or None)
formset = ImageFormSet(request.POST or None, request.FILES or None)
if form.is_valid() and formset.is_valid():
instance = form.save(commit=False)
instance.user = request.user
post_image_create(request=request, post=instance) #This function is defined above
instance.save()
for index, f in enumerate(formset.cleaned_data):
try:
photo = Extra(sequence=index+1, post=instance, image=f['image'],
image_title=f['image_title'], image_description=f['image_description'])
photo.save()
except Exception as e:
break
return redirect('posts:single', username=instance.user.username, slug=instance.slug)
else:
form = PostForm()
formset = ImageFormSet(queryset=Extra.objects.none())
context = {
'form': form,
'formset': formset,
}
return render(request, 'posts/post_form.html', context)
我的 urls.py
url(r'^new_image/$', views.post_image_create, name='new_image'),
关于如何完成这项工作的任何建议
我的模板
{% extends 'posts/post_base.html' %}
{% load bootstrap3 %}
{% load staticfiles %}
{% block postcontent %}
<head>
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet" type="text/css"/>
<link href="https://unpkg.com/filepond-plugin-image-edit/dist/filepond-plugin-image-edit.css" rel="stylesheet" type="text/css"/>
<link href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" rel="stylesheet" type="text/css"/>
<link href="{% static 'doka.min.css' %}" rel="stylesheet" type="text/css"/>
<style>
html {
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;
font-size: 1em;
}
body {
padding: 2em;
max-width: 30em;
}
</style>
</head>
<body>
<div class="container">
<h2> Add a new Recipe</h2>
<form action="" method="post" enctype="multipart/form-data" id="form">
{% csrf_token %}
{% bootstrap_form form %}
<img alt="" id="preview" src="" width="100" />
<img alt="" id="new_image" src="" style="display: none;" />
{{formset.management_form}}
<h3 class="text-danger">You must be present in at least 1 image making the dish. With your face clearly visible and
matching your profile picture
</h3>
<h5>(Remember a picture is worth a thousand words) try to add as many extra images as possible
<span class="text-danger"><b>(Minimum 2)</b></span>.
People love to see how its made. Try not to add terms/language which only a few people understand.
Please add your own images. The ones you took while making the dish. Do not copy images</h5>
{% for f in formset %}
<div style="border-style: inset; padding:20px; display: none;" id="form{{forloop.counter}}" >
<p class="text-warning">Extra Image {{forloop.counter}}</p>
{% bootstrap_form f %}
<img alt="" src="" width="60" id="extra_image{{forloop.counter}}" />
</div>
{% endfor %}
<br/><button type="button" id="add_more" onclick="myFunction()">Add more images</button>
<input type="submit" class="btn btn-primary" value="Post" style="float:right;"/>
</form>
</div>
<script>
[
{supported: 'Promise' in window, fill: 'https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js'},
{supported: 'fetch' in window, fill: 'https://cdn.jsdelivr.net/npm/fetch-polyfill@0.8.2/fetch.min.js'},
{supported: 'CustomEvent' in window && 'log10' in Math && 'sign' in Math && 'assign' in Object && 'from' in Array &&
['find', 'findIndex', 'includes'].reduce(function(previous, prop) { return (prop in Array.prototype) ? previous : false; }, true), fill: 'doka.polyfill.min.js'}
].forEach(function(p) {
if (p.supported) return;
document.write('<script src="' + p.fill + '"><\/script>');
});
</script>
<script src="https://unpkg.com/filepond-plugin-image-edit"></script>
<script src="https://unpkg.com/filepond-plugin-image-preview"></script>
<script src="https://unpkg.com/filepond-plugin-image-exif-orientation"></script>
<script src="https://unpkg.com/filepond-plugin-image-crop"></script>
<script src="https://unpkg.com/filepond-plugin-image-resize"></script>
<script src="https://unpkg.com/filepond-plugin-image-transform"></script>
<script src="https://unpkg.com/filepond"></script>
<script src="{% static 'doka.min.js' %}"></script>
<script>
FilePond.registerPlugin(
FilePondPluginImageExifOrientation,
FilePondPluginImagePreview,
FilePondPluginImageCrop,
FilePondPluginImageResize,
FilePondPluginImageTransform,
FilePondPluginImageEdit
);
// Below is my failed attempt to tackle the csrf issue
const csrftoken = $("[name=csrfmiddlewaretoken]").val();
FilePond.setOptions({
server: {
url: 'http://127.0.0.1:8000',
process: {
url: 'new_image/',
method: 'POST',
withCredentials: false,
headers: {
headers:{
"X-CSRFToken": csrftoken
},
timeout: 7000,
onload: null,
onerror: null,
ondata: null
}
}
}});
// This is the expanded version of the Javascript code that uploads the image
FilePond.create(document.querySelector('input[type="file"]'), {
// configure Doka
imageEditEditor: Doka.create({
cropAspectRatioOptions: [
{
label: 'Free',
value: null
}
]
})
});
The below codes are exacty like the one above. I have just minimised it
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
FilePond.create(document.querySelector('input[type="file"]'), {...});
// ignore this part This is just to have a new form appear when the add more image button is pressed. Default is 3 images
<script>
document.getElementById("form1").style.display = "block";
document.getElementById("form2").style.display = "block";
document.getElementById("form3").style.display = "block";
let x = 0;
let i = 4;
function myFunction() {
if( x < 13) {
x = i ++
}
document.getElementById("form"+x+"").style.display = "block";
}
</script>
</body>
{% endblock %}
我没有添加 forms.py,因为它们不相关
【问题讨论】:
-
你能显示表单所在的html部分吗?
-
@Jay 我已经添加了表单的 html 部分。很抱歉我应该从一开始就添加它
标签: django python-3.x django-forms django-views