Программное сохранение изображения в Django ImageField - PullRequest
186 голосов
/ 20 августа 2009

Хорошо, я попробовал почти все и не могу заставить это работать.

  • У меня есть модель Django с ImageField на ней
  • У меня есть код, который загружает изображение через HTTP (проверено и работает)
  • Изображение сохраняется непосредственно в папку upload_to (закачка - та, которая установлена ​​в ImageField)
  • Все, что мне нужно сделать, это связать уже существующий путь к файлу изображения с ImageField

Я написал этот код о 6 разных способах.

Проблема, с которой я сталкиваюсь, заключается в том, что весь код, который я пишу, приводит к следующему поведению: (1) Django создаст второй файл, (2) переименует новый файл, добавив _ в конец имени файла, затем (3) не перенесет никаких данных, оставив его в основном пустым переименованным файлом. В пути «upload_to» осталось 2 файла, один из которых является фактическим изображением, а другой - именем изображения, но пустым, и, конечно, путь ImageField установлен на пустой файл, который пытается создать Django. .

Если это неясно, я попытаюсь проиллюстрировать:

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

Как я могу сделать это, не пытаясь Django повторно сохранить файл? Что мне действительно нравится, так это что-то в этом роде ...

model.ImageField.path = generated_image_path

... но, конечно, это не работает.

И да, я рассмотрел другие вопросы, такие как этот , а также документ django по File

UPDATE После дальнейшего тестирования, это происходит только при работе под Apache на Windows Server. Во время работы под «runserver» на XP он не выполняет это поведение.

Я в тупике.

Вот код, который успешно работает на XP ...

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

Ответы [ 14 ]

156 голосов
/ 21 августа 2009

У меня есть код, который выбирает изображение из Интернета и сохраняет его в модели. Важные биты:

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

Это немного сбивает с толку, потому что это извлечено из моей модели и немного вне контекста, но важные части:

  • Извлеченное из Интернета изображение не хранится в папке upload_to, вместо этого оно сохраняется как временный файл с помощью функции urllib.urlretrieve () и позднее удаляется.
  • Метод ImageField.save () принимает имя файла (бит os.path.basename) и объект django.core.files.File.

Дайте мне знать, если у вас есть вопросы или вам нужны разъяснения.

Изменить: для ясности, вот модель (за вычетом любых требуемых операторов импорта):

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()
84 голосов
/ 16 октября 2012

Очень просто, если модель еще не создана:

Сначала , скопируйте файл изображения в путь загрузки (предполагается = 'путь /' в следующем фрагменте).

Второй , используйте что-то вроде:

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

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

протестировано и работает в django 1.4, может работать и для существующей модели.

39 голосов
/ 24 июля 2010

Просто небольшое замечание. tvon answer работает, но, если вы работаете с Windows, вы, вероятно, захотите open() файл с 'rb'. Как это:

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

, иначе файл будет усечен на первом 0x1A байте.

14 голосов
/ 04 июля 2011

Вот метод, который хорошо работает и позволяет также преобразовать файл в определенный формат (чтобы избежать ошибки «невозможно записать режим P как 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)

где изображение - это django ImageField или your_model_instance.image Вот пример использования:

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

Надеюсь, это поможет

10 голосов
/ 26 сентября 2013

Хорошо, если все, что вам нужно сделать, это связать уже существующий путь к файлу изображения с ImageField, то это решение может быть полезным:

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

Что ж, если честно, уже существующий файл изображения не будет связан с ImageField, но копия этого файла будет создана в upload_to dir как 'imgfilename.jpg' и будет связана с ImageField.

10 голосов
/ 20 ноября 2010

Я создал собственное хранилище, которое не будет сохранять файл на диск:

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

Затем в моих моделях для моего ImageField я использовал новое пользовательское хранилище:

from custom_storage import CustomStorage

custom_store = CustomStorage()

class Image(models.Model):
    thumb = models.ImageField(storage=custom_store, upload_to='/some/path')
7 голосов
/ 29 августа 2017

Самое простое на мой взгляд решение:

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)
7 голосов
/ 10 марта 2011

Если вы хотите просто «установить» фактическое имя файла, не неся при этом затрат на загрузку и повторное сохранение файла (!!) или прибегая к использованию поля char (!!!), вы можете попробовать что-нибудь как это -

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

Это осветит ваш model_instance.myfile.url и все остальные, как если бы вы действительно загрузили файл.

Как говорит @ t-stone, мы действительно хотим установить instance.myfile.path = 'my-filename.jpg', но Django в настоящее время не поддерживает это.

2 голосов
/ 21 августа 2009

Возможно, это не тот ответ, который вы ищете. но вы можете использовать charfield для хранения пути к файлу вместо ImageFile. Таким образом, вы можете программно связать загруженное изображение с полем без воссоздания файла.

1 голос
/ 16 сентября 2016
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()
...