我写了一个小应用程序,它将一个.gif作为上传,并使用Pillow将gif分割成帧。我通过Document
模型保存.gif文件,并通过DocumentImage
模型保存框架。目前,该应用程序保存.gif对象并为每个框架创建一个对象。我想要的是将所有帧保存到单个对象中,并将该对象链接到gif对象。这是我到目前为止所做的:
views.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}
)
models.py
def content_file_name(instance, filename):
ext = ''.join(filename.split())[:-4]
foldername = "%s/%s" % (uuid.uuid4(), ext)
return '/'.join(['documents', str(foldername), filename])
class Document(models.Model):
docfile = models.ImageField(upload_to=content_file_name)
class DocumentImage(models.Model):
imagefile = models.ImageField(upload_to=content_file_name)
image = models.ForeignKey(Document, related_name='Image', null=True, on_delete=models.CASCADE)
我是Django的新手,不知道我问的问题是否可能,但它似乎是一个基本功能。我已经阅读了关于多对一关系的Django文档,但不太了解外键是如何链接的。谢谢你的时间。
答案 0 :(得分:1)
我想要的是将所有帧保存到单个对象中
我认为你的意思是&#34;每一帧都被保存到一个不同的对象中。 ?
并将该对象链接到gif对象。
有点OT,但使用正确的命名确实有帮助。在这里你提到一个&#34; gif对象&#34;但是您的模型名称(带有上传的gif文件的名称)是&#34; Document&#34;,并且各个帧的模型是&#34; DocumentImage&#34; - 但这些是由名为&#34; create_gif&#34;的函数创建的。这既不会创建一个gif而不是Document
,但会将gif拆分成帧并创建DocumentImage
个实例......它只会导致混乱。
现在回到你的问题 - 根据我的理解,DocumentImage
实例没有链接到匹配的Document
实例。实际上,您几乎就在那里,您只需将Document
实例传递给DocumentImage
个实例。最简单的方法是将它传递给你的(名字很差的)create_gif()
函数,然后传递给DocumentImage
构造函数。
现在你的代码存在很多其他问题 - 不一定是&#34;阻塞&#34;问题(你的代码可能有效)但它真的没有充分利用Python和Django的功能 - 而且有些评论充其量也是误导性的(例如&#34;#如果它没有&#创建一个文件夹) #39; t存在&#34;当您实际打开图像文件时)。
第一个明显的问题是使用表格(您没有发布)但在视图中进行额外验证 - 验证是您表格的首要任务。
第二个问题是依赖文件扩展名进行文件类型验证 - 这是不可靠和不安全的。您可能希望使用类似imghdr的内容。
第三个问题是,当您可以使用ModelForm
时,手动创建Document
实例。
然后你有文件名/扩展/路径处理代码既不可靠也不可移植,一个裸的except子句不仅隐藏有用的调试内容,而且无法正确处理异常,这个&#34;手册&#34 ; while create_gif()
中的while循环,当你应该使用for循环和enumerate
来获取索引时,循环中的函数调用并不依赖于循环的变量( IOW:这将在每次迭代时产生相同的结果。)
这是您的代码的修订版。 它完全没有经过测试(这意味着非常肯定有错误),但它可以帮助您解决问题并提高代码的质量。
模型
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}
)
编辑:您在评论中说明:
我希望在一个对象下有多个图像。
如果您的意思是将所有帧图像作为同一模型实例的不同字段,那么不,您不能。好吧,你可以为你的模型添加数千ImageField
,但无论你添加了多少ImageField
,有一天你仍然达到了限制(那里&# 39; s AFAIK对gif中的帧数没有理论限制,如果有技术问题,它可能对rdbms来说太高了支持它,2 /它将是一个糟糕的kludge由于数据库表中的字段数量,处理和/ /你会受到非常严重的性能影响。
总而言之,你做了什么(一个&#34;主人&#34; Document
模型与0-N相关DocumentImage
slave
模型)是正确的方法为您的用例建模。请注意,一旦在Document
个实例中正确设置DocumentImage
外键,获取给定Document
实例的帧就像mydoc.images.all()
一样简单(nb使用我更正的模型),并且DocumentImage
保证属于一个Document
(这里再次使用我的正确型号代码),因此您在技术上接近于&#34;在一个单一对象下的多个图像&#34;你可以得到。在纯Python中的FWIW(没有rdbms和Django模型)你仍然将它建模为一个Document
对象,其中list
有一个集合(image
将是一个明显的候选者)(框架)对象所以它没有那么大的差异。