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

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

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

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

проблема я бегу в это весь код, который я пишу, приводит к следующему поведению: (1) Django сделает 2-й файл, (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 на

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

Я в тупик.

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

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

14 ответов


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

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, вместо этого хранится в виде tempfile по urllib.urlretrieve (), а потом отказались.
  • В ImageField.метод save () принимает имя файла (ОС.путь.basename bit) и Джанго.ядро.файлы.Объект file.

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

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

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

супер легко, если модель еще не была создана:

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

второй, например:

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

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

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


просто небольшое замечание. tvon ответ работает, но, если вы работаете на 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 байт.


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

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

надеюсь, что это помогает


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


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

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

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

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

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

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


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

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)

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


можно попробовать:

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

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

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

вы можете использовать Django Rest framework и python запросы библиотека для программного сохранения изображения в 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) 

работает! Вы можете сохранить изображение с помощью FileSystemStorage. проверьте пример ниже

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)