93

请原谅我丑陋的英语;-)

想象一下这个非常简单的模型:

class Photo(models.Model):
    image = models.ImageField('Label', upload_to='path/')

我想从图像 URL 创建照片(即,不是在 django 管理站点中手动创建)。

我认为我需要做这样的事情:

from myapp.models import Photo
import urllib

img_url = 'http://www.site.com/image.jpg'
img = urllib.urlopen(img_url)
# Here I need to retrieve the image (as the same way that if I put it in an input from admin site)
photo = Photo.objects.create(image=image)

我希望我已经很好地解释了这个问题,如果没有告诉我。

谢谢 :)

编辑 :

这可能有效,但我不知道如何转换content为 django 文件:

from urlparse import urlparse
import urllib2
from django.core.files import File

photo = Photo()
img_url = 'http://i.ytimg.com/vi/GPpN5YUNDeI/default.jpg'
name = urlparse(img_url).path.split('/')[-1]
content = urllib2.urlopen(img_url).read()

# problem: content must be an instance of File
photo.image.save(name, content, save=True)
4

9 回答 9

98

我刚刚为同样的问题创建了http://www.djangosnippets.org/snippets/1890/ 。该代码类似于上面的简洁答案,除了它使用 urllib2.urlopen 因为 urllib.urlretrieve 默认不执行任何错误处理,因此很容易获取 404/500 页面的内容而不是您需要的内容。您可以创建回调函数和自定义 URLOpener 子类,但我发现创建自己的临时文件更容易,如下所示:

from django.core.files import File
from django.core.files.temp import NamedTemporaryFile

img_temp = NamedTemporaryFile(delete=True)
img_temp.write(urllib2.urlopen(url).read())
img_temp.flush()

im.file.save(img_filename, File(img_temp))
于 2010-01-26T18:59:14.870 回答
32

from myapp.models import Photo
import urllib
from urlparse import urlparse
from django.core.files import File

img_url = 'http://www.site.com/image.jpg'

photo = Photo()    # set any other fields, but don't commit to DB (ie. don't save())
name = urlparse(img_url).path.split('/')[-1]
content = urllib.urlretrieve(img_url)

# See also: http://docs.djangoproject.com/en/dev/ref/files/file/
photo.image.save(name, File(open(content[0])), save=True)

于 2009-09-08T10:54:40.170 回答
16

结合 Chris Adams 和 Stan 所说的内容并更新内容以在 Python 3 上运行,如果您安装Requests,您可以执行以下操作:

from urllib.parse import urlparse
import requests
from django.core.files.base import ContentFile
from myapp.models import Photo

img_url = 'http://www.example.com/image.jpg'
name = urlparse(img_url).path.split('/')[-1]

photo = Photo() # set any other fields, but don't commit to DB (ie. don't save())

response = requests.get(img_url)
if response.status_code == 200:
    photo.image.save(name, ContentFile(response.content), save=True)

Django 的 ContentFile 文档Requests 的文件下载示例中的更多相关文档。

于 2017-02-20T10:14:44.183 回答
6

ImageField只是一个字符串,相对于您的MEDIA_ROOT设置的路径。只需保存文件(您可能想使用 PIL 来检查它是否为图像)并使用其文件名填充该字段。

因此,它与您的代码不同之处在于您需要将输出保存urllib.urlopen到文件(在您的媒体位置内),计算出路径,将其保存到您的模型中。

于 2009-09-08T10:35:58.347 回答
5

我在 Python 3 上这样做,它应该可以在 Python 2 上进行简单的修改。这是基于我知道我正在检索的文件很小。如果您的不是,我可能会建议将响应写入文件而不是在内存中缓冲。

需要 BytesIO 是因为 Django 在文件对象上调用 seek(),而 urlopen 响应不支持搜索。您可以将 read() 返回的字节对象传递给 Django 的 ContentFile。

from io import BytesIO
from urllib.request import urlopen

from django.core.files import File


# url, filename, model_instance assumed to be provided
response = urlopen(url)
io = BytesIO(response.read())
model_instance.image_field.save(filename, File(io))
于 2014-10-13T00:40:01.900 回答
3

最近我在 python 3 和 Django 3 中使用了以下方法,也许这对其他人也很有趣。它类似于 Chris Adams 解决方案,但对我来说它不再起作用了。

import urllib.request
from django.core.files.uploadedfile import SimpleUploadedFile
from urllib.parse import urlparse

from demoapp import models


img_url = 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Stack_Overflow_logo.png'
basename = urlparse(img_url).path.split('/')[-1]
tmpfile, _ = urllib.request.urlretrieve(img_url)

new_image = models.ModelWithImageOrFileField()
new_image.title = 'Foo bar'
new_image.file = SimpleUploadedFile(basename, open(tmpfile, "rb").read())
new_image.save()
于 2020-04-04T22:16:45.457 回答
2

刚刚发现不用生成临时文件:

将 url 内容直接从 django 流式传输到 minio

我必须将我的文件存储在 minio 中,并且 django docker 容器没有太多磁盘空间并且需要下载大视频文件,所以这对我很有帮助。

于 2020-01-31T07:53:34.230 回答
0

这个问题已经过去了将近 11 年,最知名的答案已经发布。感谢@chris-adams 的回复。我只是重新发布相同的答案以及更新的软件包和支持。

#! /usr/bin/python3
# lib/utils.py

import urllib3                                          # http Request Package.
from typing import Optional

from django.core.files import File                      # Handle Files in Django
from django.core.files.temp import NamedTemporaryFile   # handling temporary files.


def fetch_image(url: str, instance: models.Model, field: str, name: Optional[str]=None):
    """
    fetch_image Fetches an image URL and adds it to the model field.
    the parameter instance does not need to be a saved instance. 

    :url: str = A valid image URL.
    :instance: django.db.models.Model = Expecting a model with image field or file field. 
    :field: str = image / file field name as string; 
    [name:str] = Preferred file name, such as product slug or something.  

    :return: updated instance as django.db.models.Model, status of updation as bool.
    
    """
    
    conn = urllib3.PoolManager()
    response = conn.request('GET', url)
    if response.status <> 200:
        print("[X] 404! IMAGE NOT FOUND")
        print(f"TraceBack: {url}")
        return instance, False
    
    file_obj = NamedTemporaryFile(delete=True)
    file_obj.write( response.data )
    file_obj.flush()

    img_format = url.split('.')[-1]
    
    if name is None:
        name = url.split('/')[-1]
    
    if not name.endswith(img_format):
        name += f'.{img_format}'
    
    django_file_obj = File(file_obj)
    (getattr(instance, field)).save(name, django_file_obj)
    return instance, True
    

在 Python 3.7.5 中使用 Django==2.2.12 测试


if __name__ == '__main__':
    instance = ProductImage()
    url = "https://www.publicdomainpictures.net/pictures/320000/velka/background-image.png"
    instance, saved = fetch_image(url, instance, field='banner_image', name='intented-image-slug')
    status = ["FAILED! ", "SUCCESS! "][saved]
    print(status, instance.banner_image and instance.banner_image.path)
    instance.delete()

于 2021-08-17T08:52:56.790 回答
-5

这是正确的工作方式

class Product(models.Model):
    upload_path = 'media/product'
    image = models.ImageField(upload_to=upload_path, null=True, blank=True)
    image_url = models.URLField(null=True, blank=True)

    def save(self, *args, **kwargs):
        if self.image_url:
            import urllib, os
            from urlparse import urlparse
            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(upload_path, filename)
            self.image_url = ''
            super(Product, self).save()
于 2012-10-28T03:49:13.240 回答