web-dev-qa-db-fra.com

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

veuillez m'excuser pour mon laid 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 Django admin)).

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.

Je vous remercie :)

Modifier :

Cela peut fonctionner mais je ne sais pas comment convertir content en 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)
82
user166648

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 car urllib.urlretrieve n'effectue aucune gestion des erreurs par défaut, il est donc facile d'obtenir le contenu d'une page 404/500 au lieu de ce dont vous aviez besoin. Vous pouvez créer une fonction de rappel et une sous-classe URLOpener personnalisée, mais j'ai trouvé plus facile 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))
95
Chris Adams

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

En combinant ce que Chris Adams et Stan ont dit et en mettant à jour les choses sur lesquelles travailler Python 3, si vous installez Requests vous pouvez faire quelque chose comme ceci:

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)

Documents plus pertinents dans documentation ContentFile de Django et téléchargement du fichier de requêtes exemple.

8
metakermit

ImageField n'est qu'une chaîne, un chemin relatif à votre MEDIA_ROOT réglage. Enregistrez simplement le fichier (vous voudrez peut-être utiliser PIL pour vérifier qu'il s'agit d'une image) et remplissez le champ avec son nom de fichier.

Il diffère donc de votre code en ce que vous devez enregistrer la sortie de votre urllib.urlopen au fichier (à l'intérieur de votre emplacement multimédia), déterminez le chemin d'accès, enregistrez-le dans votre modèle.

5
Oli

Je le fais de cette façon sur Python 3, qui devrait fonctionner avec des adaptations simples sur Python 2. Ceci est basé sur ma connaissance que les fichiers que je récupère Si la vôtre ne l'est 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 parce que Django appelle search () sur l'objet fichier et les réponses urlopen ne prennent pas en charge la recherche. Vous pouvez plutôt transmettre l'objet bytes retourné par read () au 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))
3
jbg