web-dev-qa-db-fra.com

Enregistrer une image par programme dans Django ImageField

Ok, j'ai essayé à peu près tout et je n'arrive pas à faire fonctionner ça.

  • J'ai un modèle Django avec un ImageField dessus
  • 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' (upload_to étant celui défini sur ImageField)
  • Tout ce que je dois faire est d’associer le chemin de fichier d’image existant à ImageField

J'ai écrit ce code de 6 manières différentes.

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

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

## 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 essaye de re-stocker le fichier? Ce que j'aimerais vraiment, c'est quelque chose à cet effet ...

model.ImageField.path = generated_image_path

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

Et oui, je suis passé par les autres questions ici comme celle-ci ainsi que le doc 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. Lorsqu'il est exécuté sous le serveur d'exécution sur XP, il n'exécute pas ce comportement. 

Je suis perplexe.

Voici le code qui tourne avec succès sur XP ...

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

J'ai un code qui récupère une image sur le Web et la stocke dans un modèle. Les bits importants sont:

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 déroutant parce que cela est extrait de mon modèle et un peu hors contexte, mais les parties importantes sont:

  • L'image extraite du Web est non stockée dans le dossier upload_to. Elle est plutôt stockée sous forme de fichier temporaire par urllib.urlretrieve () et ensuite supprimée.
  • 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 besoin de clarification.

Edit: par souci de clarté, voici le modèle (moins les instructions 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()
151
tvon

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

Tout d’abord , copiez votre fichier image sur le chemin de téléchargement (supposé = '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 également fonctionner pour un modèle existant.

80
Rabih Kodeih

Juste une petite remarque. tvon answer fonctionne, mais si vous travaillez sur Windows, vous voudrez 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 votre fichier sera tronqué au premier octet 0x1A

38
Tiago Almeida

Voici une méthode qui fonctionne bien et vous permet de convertir le fichier dans un certain format également (pour éviter l'erreur "ne peut pas écrire en mode P en tant que 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_instance_modèle.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 t'aides

14
michalk

Ok, si tout ce que vous avez à faire est d’associer le chemin de fichier d’image existant à ImageField, cette solution peut être utile:

from Django.core.files.base import ContentFile

with open('/path/to/already/existing/file') as f:
  data = f.read()

# obj.image is the ImageField
obj.image.save('imgfilename.jpg', ContentFile(data))

Eh bien, s’il est sérieux, le fichier image existant ne sera pas associé à ImageField, mais la copie de ce fichier sera créée dans le répertoire upload_to sous la forme 'imgfilename.jpg' et sera associée à ImageField.

10
rmnff

Ce que j'ai fait était de créer mon propre stockage qui ne sauvegardera simplement 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')
10
Nicu Surdu

Si vous voulez simplement "définir" le nom du fichier réel, sans encourir la surcharge du chargement et de la ré-enregistrement du fichier (!!), ni le recours à un champ de caractères (!!!), vous voudrez peut-être essayer quelque chose comme ceci - -

model_instance.myfile = model_instance.myfile.field.attr_class(model_instance, model_instance.myfile.field, 'my-filename.jpg')

Cela éclairera votre model_instance.myfile.url et tous les autres comme si vous aviez réellement téléchargé le fichier.

Comme @ t-stone le dit, ce que nous voulons vraiment, c'est pouvoir définir instance.myfile.path = 'mon-nom de fichier.jpg', mais Django ne le supporte pas pour le moment.

7
s29

La solution la plus simple à mon avis: 

from Django.core.files import File

with open('path_to_file', 'r') as f:   # use 'rb' mode for python3
    data = File(f)
    model.image.save('filename', data, True)
6
Ivan Semochkin

Ce n'est peut-être pas la réponse que vous recherchez. mais vous pouvez utiliser charfield pour stocker le chemin du fichier au lieu de ImageFile. De cette manière, vous pouvez associer par programme l’image téléchargée au champ sans recréer le fichier.

2
Mohamed
class Tweet_photos(models.Model):
upload_path='absolute path'
image=models.ImageField(upload_to=upload_path)
image_url = models.URLField(null=True, blank=True)
def save(self, *args, **kwargs):
    if self.image_url:
        import urllib, os
        from urlparse import urlparse
        file_save_dir = self.upload_path
        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(file_save_dir, filename)
        self.image_url = ''
    super(Tweet_photos, self).save()
1
sawan gupta

Tu peux essayer:

model.ImageField.path = os.path.join('/Upload', generated_image_path)
1
panda
class Pin(models.Model):
    """Pin Class"""
    image_link = models.CharField(max_length=255, null=True, blank=True)
    image = models.ImageField(upload_to='images/', blank=True)
    title = models.CharField(max_length=255, null=True, blank=True)
    source_name = models.CharField(max_length=255, null=True, blank=True)
    source_link = models.CharField(max_length=255, null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    tags = models.ForeignKey(Tag, blank=True, null=True)

    def __unicode__(self):
        """Unicode class."""
        return unicode(self.image_link)

    def save(self, *args, **kwargs):
        """Store image locally if we have a URL"""
        if self.image_link and not self.image:
            result = urllib.urlretrieve(self.image_link)
            self.image.save(os.path.basename(self.image_link), File(open(result[0], 'r')))
            self.save()
            super(Pin, self).save()
1
Nishant

Vous pouvez utiliser le framework Django REST et python Requests library pour enregistrer par programme une image dans Django ImageField

Voici un exemple:

import requests


def upload_image():
    # PATH TO Django REST API
    url = "http://127.0.0.1:8080/api/gallery/"

    # MODEL FIELDS DATA
    data = {'first_name': "Rajiv", 'last_name': "Sharma"}

    #  UPLOAD FILES THROUGH REST API
    photo = open('/path/to/photo'), 'rb')
    resume = open('/path/to/resume'), 'rb')
    files = {'photo': photo, 'resume': resume}

    request = requests.post(url, data=data, files=files)
    print(request.status_code, request.reason) 
0
Rajiv Sharma

Travailler! Vous pouvez enregistrer une image à l’aide de FileSystemStorage . Consultez l’exemple ci-dessous

def upload_pic(request):
if request.method == 'POST' and request.FILES['photo']:
    photo = request.FILES['photo']
    name = request.FILES['photo'].name
    fs = FileSystemStorage()
##### you can update file saving location too by adding line below #####
    fs.base_location = fs.base_location+'/company_coverphotos'
##################
    filename = fs.save(name, photo)
    uploaded_file_url = fs.url(filename)+'/company_coverphotos'
    Profile.objects.filter(user=request.user).update(photo=photo)
0
Nids Barthwal