Programmatically saving image to Django ImageField
好吧,我已经尝试了几乎所有的东西,我无法让它工作。
- 我有一个带有ImageField的Django模型
- 我有通过HTTP下载图像的代码(测试和工作)
- 图像直接保存到'upload_to'文件夹中(upload_to是在ImageField上设置的文件)
- 我需要做的就是将现有的图像文件路径与ImageField相关联
我已经用6种不同的方式编写了这段代码。
我遇到的问题是我正在编写的所有代码都会导致以下行为:
(1)Django将创建第二个文件,(2)重命名新文件,在文件名末尾添加_,然后(3)不传输任何数据,基本上是一个空的重命名文件。 'upload_to'路径中剩下的是2个文件,一个是实际图像,一个是图像的名称,但是是空的,当然ImageField路径设置为Django尝试创建的空文件。
如果不清楚,我会试着说明:
1 2 3 4 5 6 7 8 9 10 | ## Image generation code runs.... /Upload generated_image.jpg 4kb ## Attempt to set the ImageField path... /Upload generated_image.jpg 4kb generated_image_.jpg 0kb ImageField.Path = /Upload/generated_image_.jpg |
如果Django没有尝试重新存储文件,我怎么能这样做呢?我真正喜欢的是这个效果......
1 | model.ImageField.path = generated_image_path |
......但当然这不起作用。
是的,我已经完成了这里的其他问题以及文件上的django doc
UPDATE
进一步测试后,它只在Windows Server上的Apache下运行时才会出现这种情况。在XP上的"runserver"下运行时,它不会执行此行为。
我很难过。
这是在XP上成功运行的代码...
1 2 3 | f = open(thumb_path, 'r') model.thumbnail = File(f) model.save() |
我有一些代码可以从网上获取图像并将其存储在模型中。重要的是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from django.core.files import File # you need this somewhere import urllib # The following actually resides in a method of my model result = urllib.urlretrieve(image_url) # image_url is a URL to an image # self.photo is the ImageField self.photo.save( os.path.basename(self.url), File(open(result[0], 'rb')) ) self.save() |
这有点令人困惑,因为它从我的模型中退出并且有点脱离背景,但重要的部分是:
- 从Web中提取的图像不存储在upload_to文件夹中,而是由urllib.urlretrieve()存储为临时文件,然后丢弃。
- ImageField.save()方法接受一个文件名(os.path.basename位)和一个django.core.files.File对象。
如果您有疑问或需要澄清,请与我们联系。
编辑:为了清楚起见,这里是模型(减去任何必需的import语句):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class CachedImage(models.Model): url = models.CharField(max_length=255, unique=True) photo = models.ImageField(upload_to=photo_path, blank=True) def cache(self): """Store image locally if we have a URL""" if self.url and not self.photo: result = urllib.urlretrieve(self.url) self.photo.save( os.path.basename(self.url), File(open(result[0], 'rb')) ) self.save() |
如果尚未创建模型,则非常简单:
首先,将您的图像文件复制到上传路径(假设=以下代码段中的'path /')。
其次,使用类似的东西:
1 2 3 4 5 6 | class Layout(models.Model): image = models.ImageField('img', upload_to='path/') layout = Layout() layout.image ="path/image.png" layout.save() |
在django 1.4中测试和工作,它也可能适用于现有模型。
只是一点点评论。 tvon答案有效但是,如果你正在使用Windows,你可能想要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class CachedImage(models.Model): url = models.CharField(max_length=255, unique=True) photo = models.ImageField(upload_to=photo_path, blank=True) def cache(self): """Store image locally if we have a URL""" if self.url and not self.photo: result = urllib.urlretrieve(self.url) self.photo.save( os.path.basename(self.url), File(open(result[0], 'rb')) ) self.save() |
或者你会在第一个
这是一个运行良好的方法,并允许您将文件转换为某种格式(以避免"无法将模式P写为JPEG"错误):
1 2 3 4 5 6 7 8 9 10 11 12 13 | import urllib2 from django.core.files.base import ContentFile from PIL import Image from StringIO import StringIO def download_image(name, image, url): input_file = StringIO(urllib2.urlopen(url).read()) output_file = StringIO() img = Image.open(input_file) if img.mode !="RGB": img = img.convert("RGB") img.save(output_file,"JPEG") image.save(name+".jpg", ContentFile(output_file.getvalue()), save=False) |
其中image是django ImageField或your_model_instance.image
这是一个用法示例:
1 2 3 | p = ProfilePhoto(user=user) download_image(str(user.id), p.image, image_url) p.save() |
希望这可以帮助
我所做的是创建我自己的存储,不会将文件保存到磁盘:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from django.core.files.storage import FileSystemStorage class CustomStorage(FileSystemStorage): def _open(self, name, mode='rb'): return File(open(self.path(name), mode)) def _save(self, name, content): # here, you should implement how the file is to be saved # like on other machines or something, and return the name of the file. # In our case, we just return the name, and disable any kind of save return name def get_available_name(self, name): return name |
然后,在我的模型中,对于我的ImageField,我使用了新的自定义存储:
1 2 3 4 5 6 | from custom_storage import CustomStorage custom_store = CustomStorage() class Image(models.Model): thumb = models.ImageField(storage=custom_store, upload_to='/some/path') |
好的,如果你需要做的就是将现有的图像文件路径与ImageField相关联,那么这个解决方案可能会有所帮助:
1 2 3 4 5 6 7 | from django.core.files.base import ContentFile with open('/path/to/already/existing/file') as f: data = f.read() # obj.image is the ImageField obj.image.save('imgfilename.jpg', ContentFile(data)) |
好吧,如果认真的话,现有的图像文件将不会与ImageField相关联,但是此文件的副本将在upload_to目录中创建为"imgfilename.jpg",并将与ImageField相关联。
在我看来,这是最简单的解决方案:
1 2 3 4 5 | from django.core.files import File with open('path_to_file', 'r') as f: # use 'rb' mode for python3 data = File(f) model.image.save('filename', data, True) |
如果你只想"设置"实际文件名,而不会产生加载和重新保存文件(!!)或使用charfield(!!!)的开销,你可能想尝试这样的事情 - -
1 | model_instance.myfile = model_instance.myfile.field.attr_class(model_instance, model_instance.myfile.field, 'my-filename.jpg') |
这将点亮你的model_instance.myfile.url以及其他所有内容,就像你实际上传了这个文件一样。
就像@ t-stone所说,我们真正想要的是能够设置instance.myfile.path ='my-filename.jpg',但Django目前不支持。
这可能不是您正在寻找的答案。但您可以使用charfield存储文件的路径而不是ImageFile。这样,您可以以编程方式将上载的图像关联到字段,而无需重新创建文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Pin(models.Model): """Pin Class""" image_link = models.CharField(max_length=255, null=True, blank=True) image = models.ImageField(upload_to='images/', blank=True) title = models.CharField(max_length=255, null=True, blank=True) source_name = models.CharField(max_length=255, null=True, blank=True) source_link = models.CharField(max_length=255, null=True, blank=True) description = models.TextField(null=True, blank=True) tags = models.ForeignKey(Tag, blank=True, null=True) def __unicode__(self): """Unicode class.""" return unicode(self.image_link) def save(self, *args, **kwargs): """Store image locally if we have a URL""" if self.image_link and not self.image: result = urllib.urlretrieve(self.image_link) self.image.save(os.path.basename(self.image_link), File(open(result[0], 'r'))) self.save() super(Pin, self).save() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class tweet_photos(models.Model): upload_path='absolute path' image=models.ImageField(upload_to=upload_path) image_url = models.URLField(null=True, blank=True) def save(self, *args, **kwargs): if self.image_url: import urllib, os from urlparse import urlparse file_save_dir = self.upload_path filename = urlparse(self.image_url).path.split('/')[-1] urllib.urlretrieve(self.image_url, os.path.join(file_save_dir, filename)) self.image = os.path.join(file_save_dir, filename) self.image_url = '' super(tweet_photos, self).save() |
你可以试试:
1 | model.ImageField.path = os.path.join('/Upload', generated_image_path) |
工作!
您可以使用FileSystemStorage保存图像。
检查下面的例子
1 2 3 4 5 6 7 8 9 10 11 | def upload_pic(request): if request.method == 'POST' and request.FILES['photo']: photo = request.FILES['photo'] name = request.FILES['photo'].name fs = FileSystemStorage() ##### you can update file saving location too by adding line below ##### fs.base_location = fs.base_location+'/company_coverphotos' ################## filename = fs.save(name, photo) uploaded_file_url = fs.url(filename)+'/company_coverphotos' Profile.objects.filter(user=request.user).update(photo=photo) |
您可以使用Django REST框架和python Requests库以编程方式将图像保存到Django ImageField
这是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import requests def upload_image(): # PATH TO DJANGO REST API url ="http://127.0.0.1:8080/api/gallery/" # MODEL FIELDS DATA data = {'first_name':"Rajiv", 'last_name':"Sharma"} # UPLOAD FILES THROUGH REST API photo = open('/path/to/photo'), 'rb') resume = open('/path/to/resume'), 'rb') files = {'photo': photo, 'resume': resume} request = requests.post(url, data=data, files=files) print(request.status_code, request.reason) |