Программно сохраняя изображение на Django ImageField

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

  • У меня есть модель Django с ImageField на ней
  • У меня есть код, который загружает изображение через HTTP (проверено и работает)
  • Изображение сохраняется непосредственно в папке 'upload_to' (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 doc на File

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

Я в тупике.

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

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

Ответ 1

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

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

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]))
                    )
            self.save()

Ответ 2

Супер простая, если модель еще не создана:

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

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

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

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

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

Ответ 3

Просто небольшое замечание. tvon отвечает, но если вы работаете с окнами, вы, вероятно, захотите открыть() файл с помощью "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.

Ответ 4

Вот этот метод, который хорошо работает и позволяет вам конвертировать файл в определенный формат (чтобы избежать ошибки "не записывать режим P как JPEG" ):

import urllib2
from django.core.files.base import ContentFile
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()

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

Ответ 5

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

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

Ответ 6

Хорошо. Если все, что вам нужно сделать, это связать уже существующий путь к файлу изображения с 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.

Ответ 7

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

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 в настоящее время не поддерживает это.

Ответ 8

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

Ответ 9

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

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)

Ответ 10

Вы можете попробовать:

model.ImageField.path = os.path.join('/Upload', generated_image_path)

Ответ 11

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

Ответ 12

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

Ответ 13

Вы можете использовать Django REST framework и python Requests, чтобы программно сохранить изображение в Django ImageField

Вот пример:

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)