Python 将从枕头创建的图像添加到单个Django对象
我写了一个小应用程序,它上传一个.gif文件,然后用枕头将gif文件分割成若干帧。我通过Python 将从枕头创建的图像添加到单个Django对象,python,django,Python,Django,我写了一个小应用程序,它上传一个.gif文件,然后用枕头将gif文件分割成若干帧。我通过文档模型保存.gif,通过文档图像模型保存帧。目前,应用程序保存.gif对象,并为每个帧创建一个对象。我想要的是将所有帧保存到单个对象中,并将该对象链接到gif对象。以下是我目前掌握的情况: 视图.py def create_gif(uploadedFile): # create a folder if it doesn't exist try: gif = Image.open('media/' +
文档
模型保存.gif,通过文档图像
模型保存帧。目前,应用程序保存.gif对象,并为每个帧创建一个对象。我想要的是将所有帧保存到单个对象中,并将该对象链接到gif对象。以下是我目前掌握的情况:
视图.py
def create_gif(uploadedFile):
# create a folder if it doesn't exist
try:
gif = Image.open('media/' + uploadedFile)
print()
except:
print('Not OK')
frames = [frame.copy() for frame in ImageSequence.Iterator(gif)]
i = 0
while (i < len(frames)):
buffer = BytesIO()
frames[i].convert('RGB').save(fp=buffer, format='JPEG')
finalImage = InMemoryUploadedFile(buffer, None, os.path.basename(uploadedFile)[:-4] + str(i) + '.png', 'image/jpeg', frames[i].tell, None)
imageToSave = DocumentImage(imagefile=finalImage)
imageToSave.save()
i += 1
def list(request):
# Handle file upload
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
if os.path.splitext(newdoc.docfile.name)[1].lower() != '.gif':
messages.add_message(request, messages.INFO, "Please select a gif")
else:
newdoc.save()
uploadedFile = newdoc.docfile.name
create_gif(uploadedFile)
messages.add_message(request, messages.INFO, "Saved")
return HttpResponseRedirect(reverse('list'))
else:
form = DocumentForm() # A empty, unbound form
# Load documents for the list page
documents = Document.objects.all()
# Render list page with the documents and the form
return render(
request,
'list.html',
{'documents': documents, 'form': form}
)
我是Django的新手,不确定我所问的是否可能,但这似乎是一个基本功能。我已经阅读了关于多对一关系的Django文档,但不太了解外键是如何链接的。谢谢你抽出时间
我想要的是将所有帧保存到单个对象中
我想你的意思是“每一帧都要保存到一个不同的对象中”
并且该对象将链接到gif对象
有点奇怪,但使用正确的命名确实有帮助。这里您提到了一个“gif对象”,但您的模型名(带有上载gif文件的模型名)是“文档”,单个帧的模型是“DocumentImage”-但这些是由名为“create_gif”的函数创建的既不创建gif也不创建文档
,而是将gif拆分为帧并创建文档图像
实例。。。这只会导致混乱
现在回到您的问题上来-据我所知,DocumentImage
实例没有链接到匹配的Document
实例。实际上,您就快到了,只需将Document
实例传递给DocumentImage
实例。最简单的方法是将其传递给(名称非常不恰当的)create\u gif()
函数,然后传递给DocumentImage
构造函数
现在你的代码还有很多其他问题——不一定是“阻塞”问题(你的代码可能会工作),但它并没有充分利用Python和Django的特性——而且有些评论充其量也会产生误导(比如在实际打开图像文件时“创建一个不存在的文件夹”)
第一个明显的问题是使用表单(您没有发布),但在视图中执行额外的验证——验证实际上是表单的首要职责
第二个问题是依赖文件扩展名进行文件类型验证——这既不可靠又不安全。您可能需要使用类似的内容
第三个问题是当您可以使用
然后你有文件名/扩展名/路径处理代码,它既不可靠也不可移植,一个裸露的except子句,它不仅隐藏了有用的调试内容,而且无法正确处理异常,这个“手册”在create_gif()中循环
当您应该使用for循环和枚举
来获取索引时,以及循环中不依赖于循环变量的函数调用(IOW:每次迭代都会产生相同的结果)
这是您的代码的修订版本这是完全未经测试的(这意味着肯定存在bug),但它应该可以帮助您解决问题并提高代码质量
模型
import os
import sys
import uuid
# etc - add missing imports here
def content_file_name(instance, filename):
basename, _ext = os.path.splitext(filename)
foldername = os.path.join(uuid.uuid4(), basename)
return os.path.join('documents', foldername, filename)
class Document(models.Model):
docfile = models.ImageField(upload_to=content_file_name)
def create_documentfiles(self):
if self.images.exists():
# XXX should be using the `logging` module instead
print >> sys.stderr, "Document {} ({}) already has images".format(self.pk, self.docfile.path)
return
gif = Image.open(self.docfile.path)
frames = [frame.copy() for frame in ImageSequence.Iterator(gif)]
basename, _ext = os.path.splitext(self.docfile.name)
for index, frame in enumerate(frames):
buffer = BytesIO()
item.convert('RGB').save(fp=buffer, format='JPEG')
destname = "{}{}.png".format(basename, index)
imagefile = SimpleUploadedFile(buffer.read(), destname, 'image/jpeg')
DocumentFile.objects.create(document=self, imagefile=imagefile)
class DocumentImage(models.Model):
imagefile = models.ImageField(upload_to=content_file_name)
document = models.ForeignKey(Document, related_name='images', on_delete=models.CASCADE)
形式
观点
编辑:您在评论中声明:
我希望在一个对象下有多个图像
如果您的意思是将所有帧图像作为同一模型实例的不同字段,则不可以。好吧,你可以在你的模型中添加数千个ImageField
,但不管你添加多少个ImageField
,总有一天你还是会达到一个极限(gif中的帧数在理论上没有限制,如果有技术上的限制,rdbms支持它可能太高了),2/这将是一个非常棘手的问题,3/由于db表中的字段数量,您将受到非常严重的性能影响
长话短说,您所做的(带有0-N相关
DocumentImage
从属模型的“主”文档
模型)是为您的用例建模的正确方法。请注意,一旦您在DocumentImage
实例中正确设置了Document
外键,获取给定Document
实例的帧就如同mydoc.images.all()
(请使用我更正的模型),并且DocumentImage
保证属于一个文档
(这里再次使用我正确的模型代码),因此您在技术上尽可能接近“单个对象下的多个图像”。在普通Python中(没有rdbms和Django模型),您仍然可以将其建模为一个文档对象,该对象具有图像的集合(一个列表将是一个明显的候选对象)(帧)对象,所以不会有太大区别。是的,这是我开始浪费时间的事情,所以我没有集中精力在函数命名和注释上。确实,create\u gif很容易误导人,但函数会这么做。我一路上改变了主意。现在,“每个帧都保存到一个不同的对象中”这就是它现在的工作方式。我希望在一个对象下有多个图像。不确定这是否可能。至于您发现的其他问题,我只是从Python开始,因此非常感谢您的批评。
import os
import sys
import uuid
# etc - add missing imports here
def content_file_name(instance, filename):
basename, _ext = os.path.splitext(filename)
foldername = os.path.join(uuid.uuid4(), basename)
return os.path.join('documents', foldername, filename)
class Document(models.Model):
docfile = models.ImageField(upload_to=content_file_name)
def create_documentfiles(self):
if self.images.exists():
# XXX should be using the `logging` module instead
print >> sys.stderr, "Document {} ({}) already has images".format(self.pk, self.docfile.path)
return
gif = Image.open(self.docfile.path)
frames = [frame.copy() for frame in ImageSequence.Iterator(gif)]
basename, _ext = os.path.splitext(self.docfile.name)
for index, frame in enumerate(frames):
buffer = BytesIO()
item.convert('RGB').save(fp=buffer, format='JPEG')
destname = "{}{}.png".format(basename, index)
imagefile = SimpleUploadedFile(buffer.read(), destname, 'image/jpeg')
DocumentFile.objects.create(document=self, imagefile=imagefile)
class DocumentImage(models.Model):
imagefile = models.ImageField(upload_to=content_file_name)
document = models.ForeignKey(Document, related_name='images', on_delete=models.CASCADE)
import imghdr
from django import forms
from myapp.models import Document
class DocumentForm(forms.ModelForm):
class meta:
model = Document
def validate_docfile(self):
file = self.cleaned_data.get("docfile")
if file:
if imghdr.what(file.read()) != "gif":
raise forms.ValidationError("Please upload a .gif file")
file.seek(0)
return file
# better not to name anything `list` - it would shadow the builtin
# `list` type
def documentlist(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = form.save()
newdoc.create_documentfiles()
messages.add_message(request, messages.INFO, "Saved")
return HttpResponseRedirect(reverse('list'))
else:
form = DocumentForm()
documents = Document.objects.all()
return render(
request,
'list.html',
{'documents': documents, 'form': form}
)