98 votes

Django : ajouter une image dans un ImageField à partir d'une url d'image

Veuillez m'excuser pour mon mauvais anglais ;-)

Imaginez ce modèle très simple :

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

Je voudrais créer une photo à partir d'une URL d'image (c'est-à-dire pas à la main dans le site d'administration de django).

Je pense que je dois faire quelque chose comme ça :

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)

J'espère que j'ai bien expliqué le problème, sinon dites-le moi.

Merci :)

Edit :

Cela peut fonctionner mais je ne sais pas comment convertir content vers un fichier 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)

101voto

Chris Adams Points 2356

Je viens de créer http://www.djangosnippets.org/snippets/1890/ pour ce même problème. Le code est similaire à la réponse de pithyless ci-dessus sauf qu'il utilise urllib2.urlopen parce que urllib.urlretrieve n'effectue aucune gestion d'erreur par défaut donc il est facile d'obtenir le contenu d'une page 404/500 au lieu de ce dont vous avez besoin. Vous pouvez créer une fonction de rappel et une sous-classe personnalisée d'URLOpener mais j'ai trouvé plus simple de créer mon propre fichier temporaire comme ceci :

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

34voto

pithyless Points 984
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)

25voto

kermit666 Points 1730

En combinant ce que Chris Adams et Stan ont dit et en mettant à jour les choses pour fonctionner avec Python 3, si vous installez Demandes vous pouvez faire quelque chose comme ça :

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)

Plus de documents pertinents dans Documentation sur le ContentFile de Django y Téléchargement des fichiers de "demandes exemple.

7voto

Oli Points 65050

ImageField est juste une chaîne, un chemin relatif à votre MEDIA_ROOT réglage. Il suffit d'enregistrer le fichier (vous pouvez utiliser le PIL pour vérifier qu'il s'agit d'une image) et de remplir le champ avec son nom de fichier.

Il diffère donc de votre code dans la mesure où vous devez sauvegarder la sortie de votre urllib.urlopen vers le fichier (dans l'emplacement de votre média), déterminez le chemin, sauvegardez-le dans votre modèle.

6voto

Je le fais de cette façon sur Python 3, ce qui devrait fonctionner avec de simples adaptations sur Python 2. Ceci est basé sur le fait que je sais que les fichiers que je récupère sont petits. Si les vôtres ne le sont pas, je recommanderais probablement d'écrire la réponse dans un fichier au lieu de la mettre en mémoire tampon.

BytesIO est nécessaire car Django appelle seek() sur l'objet fichier, et les réponses urlopen ne prennent pas en charge la recherche. Vous pouvez passer l'objet bytes renvoyé par read() au ContentFile de Django à la place.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X