2009-08-20 3 views
158

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

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

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

И да я прошел через другие вопросы здесь, как this one, а также Джанго документ на File

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

Я в тупик.

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

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

Еще один большой вопрос Django. Я сделал несколько попыток решить эту проблему без везения. Файлы, созданные в каталоге загрузки, разбиты и имеют только небольшую часть по сравнению с оригиналами (хранятся в другом месте). – westmark

+0

ваш UPDATE не работает – AmiNadimi

ответ

134

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

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.

Сообщите мне, если у вас есть вопросы или требуется уточнение.

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

tvon - Я пробовал что-то с этим, но, возможно, я дам ему еще один шаг, на самом деле у меня был код, который выглядел очень похоже на это. (Даже если это вне контекста, я вижу, как это работает). –

+1

Я бы предложил использовать синтаксис url, чтобы избежать попадания urar paramatar gunk в изображение. 'import urlparse'. 'Os.path.basename (urlparse.urlparse (self.url) .path)'. Спасибо за сообщение, было полезно. – dennmat

+1

Я получаю django.core.exceptions.SuspiciousOperation: Попытка доступа к '/images/10.jpg' отрицается. – DataGreed

2

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

+0

Да, у меня возникло соблазн отказаться от этого и либо напрямую писать в MySQL, либо просто использовать CharField(). –

37

Просто немного замечание. tvon отвечает, но если вы работаете над окнами, вы, вероятно, захотите 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 байта.

+1

Спасибо, я стараюсь забыть такие низкоуровневые детали, с которыми мы сталкиваемся. –

+0

fml ... что происходит, когда этот параметр передается на Linux-машине? –

+1

ответил на мой собственный вопрос ... извините за спам. нашел документацию для этого [здесь] (http://docs.python.org/tutorial/inputoutput.html#reading-and-writing-files). «В Unix не помешает добавить« b »в режим, поэтому вы можете использовать его платформой независимо для всех двоичных файлов». –

9

Что я сделал, чтобы создать свою собственную память, которая не будет только сохранить файл на диск:

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

Если вы хотите просто «установить» фактическое имя файла, без каких накладных расходов загрузки и повторного сохранения файла (!!), или прибегая к использованию 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 в настоящее время не поддерживает это.

+1

См. Также [предлагаемое изменение на django] (http://code.djangoproject.com/ticket/15590) – s29

+0

Если model_instance является экземпляром модель, которая содержит файл .. что делает другой «экземпляр» для? –

+0

aah, typo- thanks h3 – s29

1

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

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

Пока не работает: https://code.djangoproject.com/ticket/15590 –

14

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

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

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

Надеется, что это помогает

68

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

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

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

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

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

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

+7

Это право ответьте, вам нужно больше голосов !!! Нашли это решение [здесь] (http://andrewbrookins.com/tech/set-the-path-to-an-imagefield-in-django-manually/). –

+0

Привет. У меня вопрос. Я использую django-хранилища с бэкэндом Amazon S3. Будет ли это инициировать новую загрузку? –

+0

OP запрашивает «без попыток Django перехранить файл», и это ответ на этот вопрос! – frnhr

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

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

+0

Разве вы не открываете его как двоичный файл? –

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

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

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

Вы можете использовать Django REST framework и питон Requests библиотеку Программным сохранения изображения Джанго 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)