Django: добавить изображение в ImageField из URL-адреса изображения

пожалуйста, извините меня за мой уродливый английский ;-)

Представьте себе эту очень простую модель:

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)

person Community    schedule 08.09.2009    source источник


Ответы (8)


Я только что создал 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))
person Chris Adams    schedule 26.01.2010
comment
что делает последняя строка? откуда исходит объект im? - person priestc; 03.01.2014
comment
@priestc: im было немного кратким - в этом примере im было экземпляром модели, а file было невообразимым именем FileField/ImageField в этом экземпляре. Фактическая документация API здесь имеет значение — этот метод должен работать везде, где у вас есть объект Django File, привязанный к объекту: docs.djangoproject.com/en/1.5/ref/files/file/ - person Chris Adams; 03.01.2014
comment
Временный файл не нужен. Используя requests вместо urllib2, вы можете сделать: image_content = ContentFile(requests.get(url_image).content), а затем obj.my_image.save("foo.jpg", image_content). - person Stan; 16.04.2014
comment
Стэн: запросы действительно упрощают это, но IIRC, что однострочный код будет проблемой, если вы сначала не вызовете raise_for_status(), чтобы избежать путаницы с ошибками или неполными ответами. - person Chris Adams; 22.04.2014
comment
Вероятно, было бы неплохо модернизировать это, поскольку изначально оно было написано в эпоху Django 1.1/1.2. Тем не менее, я считаю, что у ContentFile все еще есть проблема, заключающаяся в том, что он загружает весь файл в память, поэтому хорошей оптимизацией было бы использование iter_content с разумным размером фрагмента. - person Chris Adams; 22.04.2014
comment
Очень чистое решение! Большое тебе спасибо! - person PiWo; 15.10.2015
comment
@Stan, когда вы используете response.raw вместо response.content, вам даже не нужно загружать файл в память, и вы можете передать его прямо в свое файловое поле / s3 / minio. - person ephes; 31.01.2020
comment
Я получаю сообщение об ошибке AttributeError: объект «NoneType» не имеет атрибута «сохранить» при выполнении article.featured_image.save(name, ContentFile(response.content), save=True). есть идеи, почему? статья. экземпляр . - person Moon; 27.10.2020


ImageField — это просто строка, путь относительно вашей настройки MEDIA_ROOT. Просто сохраните файл (возможно, вы захотите использовать PIL, чтобы проверить, является ли он изображением) и заполните поле его именем файла.

Таким образом, он отличается от вашего кода тем, что вам нужно сохранить вывод вашего urllib.urlopen в файл (внутри вашего местоположения мультимедиа), проработать путь и сохранить его в своей модели.

person Oli    schedule 08.09.2009

Я делаю это на 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))
person jbg    schedule 13.10.2014
comment
File(io) возвращает ‹Файл: Нет› - person Susaj S N; 18.06.2019
comment
@SusajSNair Это просто потому, что у файла нет имени, а метод __repr__ для File записывает имя. Если вы предпочитаете, вы можете установить атрибут name для объекта File после его создания с помощью File(io), но, по моему опыту, это не имеет значения (кроме того, что он будет выглядеть лучше, если вы его распечатаете). ммв. - person jbg; 06.07.2019

Недавно я использую следующий подход в Python 3 и Django 3, возможно, это может быть интересно и другим. Это похоже на решение Криса Адамса, но для меня оно больше не работает.

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()
person contmp    schedule 04.04.2020
comment
Это единственное решение, которое сработало для меня (Python 3 + Django 2). Единственный несколько тривиальный комментарий заключается в том, что базовое имя может не иметь правильного расширения в каждом случае, в зависимости от URL. - person physicsAI; 07.07.2020

Только что обнаружил, что вам не нужно создавать временный файл:

Потоковая передача содержимого URL напрямую из django в minio

Мне нужно хранить свои файлы в minio и иметь док-контейнеры django без большого дискового пространства, и мне нужно загружать большие видеофайлы, так что это было очень полезно для меня.

person ephes    schedule 31.01.2020

это правильный и рабочий способ

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()
person Ekin Ertaç    schedule 28.10.2012
comment
Это не может быть правильным путем, вы обходите весь механизм хранения файлов FieldField и не обращаете внимания на API хранилища. - person Andre Bossard; 21.03.2013

person    schedule
comment
Привет, спасибо, что помог мне ;). Проблема в том (я цитирую документ): обратите внимание, что аргумент содержимого должен быть экземпляром File или подклассом File. Итак, у вас есть какое-либо решение для создания экземпляра файла с вашим контентом? - person ; 08.09.2009
comment
Проверьте новое редактирование; теперь это должен быть рабочий пример (хотя я его не тестировал) - person pithyless; 09.09.2009
comment
Как насчет моей модели, где у меня есть и другие поля. Например, URL-адрес и т. Д. И т. Д. Если id сделать model.image.save (...). как сохранить другие поля? Они не могут быть нулевыми РЕДАКТИРОВАТЬ: будет ли это что-то вроде этого? ››› car.photo.save('myphoto.jpg', contents, save=False) ››› car.save() - person Harry; 27.08.2010
comment
self.url??... что такое self.url здесь?? - person DeadDjangoDjoker; 29.05.2015
comment
@DeadDjangoDjoker Понятия не имею, вам нужно спросить человека, который редактировал мой ответ. На данный момент этому ответу 5 лет; Я вернулся к предыдущему рабочему решению для потомков, но, честно говоря, вам лучше ответить Крису Адаму. - person pithyless; 30.05.2015