Django: agregar imagen en un ImageField desde la url de la imagen

por favor disculpame por mi feo ingles 😉

Imagina este modelo tan simple:

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

Me gustaría crear una foto desde una URL de imagen (es decir, no a mano en el sitio de administración de django).

Creo que necesito hacer algo como esto:

 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) 

Espero haber explicado bien el problema, si no me lo dicen.

Gracias 🙂

Editar:

Esto puede funcionar pero no sé cómo convertir content en un archivo django:

 from urlparse import urlparse import urllib2 from django.core.files import File photo = Photo() img_url = 'http://sofes.miximages.com/python/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) 

Acabo de crear http://www.djangosnippets.org/snippets/1890/ para este mismo problema. El código es similar a la respuesta anterior sin pithyless, excepto que utiliza urllib2.urlopen porque urllib.urlretrieve no realiza ningún manejo de errores de forma predeterminada, por lo que es fácil obtener el contenido de una página 404/500 en lugar de lo que necesita. Puede crear una función de callback y una subclase de URLOpener personalizada, pero me resultó más fácil crear mi propio archivo temporal como este:

 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)) 
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)
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) 

Combinando lo que Chris Adams y Stan dijeron y actualizando las cosas para trabajar en Python 3, si instala Requests puede hacer algo como esto:

 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) 

Documentos más relevantes en la documentación de ContentFile de Django y en el ejemplo de descarga de archivos de solicitudes .

ImageField es solo una cadena, una ruta relativa a su configuración MEDIA_ROOT . Simplemente guarde el archivo (es posible que desee usar PIL para verificar que sea una imagen) y rellene el campo con su nombre de archivo.

Por lo tanto, difiere de su código en que necesita guardar la salida de urllib.urlopen en el archivo (dentro de su ubicación de medios), elaborar la ruta, guardarla en su modelo.

Lo hago de esta manera en Python 3, que debería funcionar con adaptaciones simples en Python 2. Esto se basa en mi conocimiento de que los archivos que estoy recuperando son pequeños. Si el tuyo no lo es, probablemente recomiendo escribir la respuesta en un archivo en lugar de almacenar en búfer en la memoria.

BytesIO es necesario porque Django llama a seek () en el objeto de archivo, y las respuestas urlopen no admiten la búsqueda. Podría pasar el objeto de bytes devuelto por read () al ContentFile de Django.

 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)) 

Esta es la forma correcta y de trabajo.

 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()