245 votes

Enregistrement programmé d'une image dans un champ image Django

Ok, j'ai presque tout essayé et je n'arrive pas à le faire fonctionner.

  • J'ai un modèle Django avec un ImageField.
  • J'ai un code qui télécharge une image via HTTP (testé et fonctionne)
  • L'image est enregistrée directement dans le dossier "upload_to" (le dossier "upload_to" étant celui qui est défini dans le champ "ImageField").
  • Tout ce que je dois faire, c'est associer le chemin d'accès au fichier image déjà existant avec le champ ImageField.

J'ai écrit ce code de 6 façons différentes.

Le problème que je rencontre est que tout le code que j'écris a le comportement suivant : (1) Django crée un deuxième fichier, (2) renomme le nouveau fichier en ajoutant un _ à la fin du nom du fichier, puis (3) ne transfère aucune des données, laissant un fichier vide renommé. Ce qui reste dans le chemin 'upload_to' est 2 fichiers, un qui est l'image réelle, et un qui est le nom de l'image, mais est vide, et bien sûr le chemin ImageField est défini sur le fichier vide que Django essaie de créer.

Au cas où cela ne serait pas clair, je vais essayer d'illustrer mon propos :

## Image generation code runs.... 
/Upload
     generated_image.jpg     4kb

## Attempt to set the ImageField path...
/Upload
     generated_image.jpg     4kb
     generated_image_.jpg    0kb

ImageField.Path = /Upload/generated_image_.jpg

Comment puis-je faire cela sans que Django n'essaie de re-stocker le fichier ? Ce que j'aimerais vraiment, c'est quelque chose comme ça...

model.ImageField.path = generated_image_path

...mais bien sûr, ça ne marche pas.

Et oui, j'ai parcouru les autres questions ici comme celui-ci ainsi que la documentation de django sur Fichier

UPDATE Après d'autres tests, ce comportement ne se produit que lors de l'exécution sous Apache sur Windows Server. Il n'exécute pas ce comportement lorsqu'il est exécuté sous 'runserver' sur XP.

Je suis perplexe.

Voici le code qui fonctionne avec succès sous XP...

f = open(thumb_path, 'r')
model.thumbnail = File(f)
model.save()

199voto

tvon Points 1341

J'ai un code qui va chercher une image sur le web et la stocke dans un modèle. Les éléments importants sont les suivants :

from django.core.files import File  # you need this somewhere
import urllib

# The following actually resides in a method of my model

result = urllib.urlretrieve(image_url) # image_url is a URL to an image

# self.photo is the ImageField
self.photo.save(
    os.path.basename(self.url),
    File(open(result[0], 'rb'))
    )

self.save()

C'est un peu confus parce que c'est tiré de mon modèle et un peu hors contexte, mais les parties importantes sont les suivantes :

  • L'image tirée du web est pas stocké dans le dossier upload_to, il est plutôt stocké comme un fichier temporaire par urllib.urlretrieve() et plus tard jeté.
  • La méthode ImageField.save() prend un nom de fichier (le bit os.path.basename) et un objet django.core.files.File.

Faites-moi savoir si vous avez des questions ou si vous avez besoin de précisions.

Edit : pour plus de clarté, voici le modèle (sans les déclarations d'importation requises) :

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

130voto

user698585 Points 3019

Super facile si le modèle n'a pas encore été créé :

Premier copiez votre fichier image dans le chemin d'accès au téléchargement (supposé être de type chemin/ dans l'extrait suivant).

Deuxièmement utilisez quelque chose comme :

class Layout(models.Model):
    image = models.ImageField('img', upload_to='path/')

layout = Layout()
layout.image = "path/image.png"
layout.save()

testé et fonctionnant dans django 1.4, il pourrait fonctionner également pour un modèle existant.

43voto

jumpifzero Points 595

Juste une petite remarque. La réponse de tvon fonctionne mais, si vous travaillez sous Windows, vous voulez probablement open() le fichier avec 'rb' . Comme ça :

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

ou vous aurez votre fichier tronqué au premier 0x1A octet.

18voto

michalk Points 71

Voici une méthode qui fonctionne bien et qui vous permet également de convertir le fichier dans un certain format (pour éviter l'erreur "cannot write mode P as JPEG") :

import urllib2
from django.core.files.base import ContentFile
from PIL import Image
from StringIO import StringIO

def download_image(name, image, url):
    input_file = StringIO(urllib2.urlopen(url).read())
    output_file = StringIO()
    img = Image.open(input_file)
    if img.mode != "RGB":
        img = img.convert("RGB")
    img.save(output_file, "JPEG")
    image.save(name+".jpg", ContentFile(output_file.getvalue()), save=False)

où image est le django ImageField ou votre_modèle_instance.image Voici un exemple d'utilisation :

p = ProfilePhoto(user=user)
download_image(str(user.id), p.image, image_url)
p.save()

J'espère que cela vous aidera

11voto

Nicolae Surdu Points 2045

Ce que j'ai fait, c'est de créer mon propre stockage qui n'enregistre pas le fichier sur le disque :

from django.core.files.storage import FileSystemStorage

class CustomStorage(FileSystemStorage):

    def _open(self, name, mode='rb'):
        return File(open(self.path(name), mode))

    def _save(self, name, content):
        # here, you should implement how the file is to be saved
        # like on other machines or something, and return the name of the file.
        # In our case, we just return the name, and disable any kind of save
        return name

    def get_available_name(self, name):
        return name

Ensuite, dans mes modèles, pour mon ImageField, j'ai utilisé le nouveau stockage personnalisé :

from custom_storage import CustomStorage

custom_store = CustomStorage()

class Image(models.Model):
    thumb = models.ImageField(storage=custom_store, upload_to='/some/path')

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