2015-01-30 1 views
4

Я пытаюсь создать предварительный просмотр конфигурации «overlay», хранящейся в модели django, чем будет применен позднее к другой модели. У меня нет большого опыта работы с файлами с питоном ... = (Сохранить изображение, созданное с помощью wand to django ImageField

Вот мой код:

import io 
from django.conf import settings 
from django.db import models 
from wand.image import Image 
from PIL.ImageFile import ImageFile, Parser, Image as PilImage 

class Overlay(models.Model): 
    RELATIVE_POSITIONS = (...) 
    SIZE_MODES = (...) 

    name = models.CharField(max_length=50) 
    source = models.FileField(upload_to='overlays/%Y/%m/%d') 
    sample = models.ImageField(upload_to='overlay_samples/%Y/%m/%d', blank=True) 
    px = models.SmallIntegerField(default=0) 
    py = models.SmallIntegerField(default=0) 
    position = models.CharField(max_length=2, choices=RELATIVE_POSITIONS) 
    width = models.SmallIntegerField(default=0) 
    height = models.SmallIntegerField(default=0) 
    size_mode = models.CharField(max_length=1, choices=SIZE_MODES, default='B') 
    last_edit = models.DateTimeField(auto_now=True) 

    def generate_sample(self): 
     """ 
     Generates the sample image and saves it in the "sample" field model 
     :return: void 
     """ 
     base_pic = Image(filename=os.path.join(settings.BASE_DIR, 'girl.jpg')) 
     overlay_pic = Image(file=self.source) 
     result_pic = io.BytesIO() 
     pil_parser = Parser() 

     if self.width or self.height: 
      resize_args = {} 
      if self.width: 
       resize_args['width'] = self.width 
      if self.height: 
       resize_args['height'] = self.height 
      overlay_pic.resize(**resize_args) 
      base_pic.composite(overlay_pic, self.px, self.py) 
      base_pic.save(file=result_pic) 

     result_pic.seek(0) 
     while True: 
      s = result_pic.read(1024) 
      if not s: 
       break 
      pil_parser.feed(s) 

     pil_result_pic = pil_parser.close() 
     self.sample.save(self.name, pil_result_pic, False) 

    def save(self, force_insert=False, force_update=False, using=None, update_fields=None): 
     self.generate_sample() 
     super(Overlay, self).save(force_insert, force_update, using, update_fields) 

Но я получаю AttributeError прочитать здесь часть на моем Джанго отладки данных:

/usr/local/lib/python2.7/dist-packages/django/core/files/utils.py in <lambda> 

    """ 
    encoding = property(lambda self: self.file.encoding) 
    fileno = property(lambda self: self.file.fileno) 
    flush = property(lambda self: self.file.flush) 
    isatty = property(lambda self: self.file.isatty) 
    newlines = property(lambda self: self.file.newlines) 
    read = property(lambda self: self.file.read) 
    readinto = property(lambda self: self.file.readinto) 
    readline = property(lambda self: self.file.readline) 
    readlines = property(lambda self: self.file.readlines) 
    seek = property(lambda self: self.file.seek) 
    softspace = property(lambda self: self.file.softspace) 
    tell = property(lambda self: self.file.tell) 

▼ Местные вары значения переменного

self <File: None> 



/usr/local/lib/python2.7/dist-packages/PIL/Image.py in __getattr__ 

     # numpy array interface support 
     new = {} 
     shape, typestr = _conv_type_shape(self) 
     new['shape'] = shape 
     new['typestr'] = typestr 
     new['data'] = self.tobytes() 
     return new 
    raise AttributeError(name) 

def __getstate__(self): 
    return [ 
     self.info, 
     self.mode, 
     self.size, 

▼ местных вары Переменная стоимость

self <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1080x1618 at 0x7F1429291248> 
name 'read' 

Что случилось?

ответ

2

Решенный!

Такие, как @Alexey Kuleshevich, говорили, что django FileField нуждаются в файле objeto, но чего не было, так это то, что мы должны сначала сохранить изображение в файл на диске или в памяти, так как предположим, что это лучшая память ... так вот окончательное решение. Я думаю, что можно было бы улучшить, чтобы не использовать два шага «преобразования»

from django.core.files.base import ContentFile 

и в методе:

result_pic = io.BytesIO() 
    pil_parser = Parser() 

    ... 
    overlay_pic.resize(**resize_args) 
    base_pic.composite(overlay_pic, self.px, self.py) 
    base_pic.save(file=result_pic) 

    result_pic.seek(0) 
    while True: 
     s = result_pic.read(1024) 
     if not s: 
      break 
     pil_parser.feed(s) 

    result_pic = io.BytesIO() 
    pil_result_pic = pil_parser.close() 
    pil_result_pic.save(result_pic, format='JPEG') 
    django_file = ContentFile(result_pic.getvalue()) 
    self.sample.save(self.name, django_file, False) 

Благодаря этому ответу: How do you convert a PIL Image to a Django File?

0

Если вы сохраняете файл в ImageField или FileField, вам необходимо убедиться, что это объект Django File. Здесь ссылка на документацию: https://docs.djangoproject.com/en/1.7/ref/models/fields/#filefield-and-fieldfile

from django.core.files import File 

и в методе:

def generate_sample(self): 
    ... 
    pil_result_pic = pil_parser.close() 
    self.sample.save(self.name, File(pil_result_pic), False) 

В противном случае это выглядит хорошо, хотя я мог бы что-то пропустил. Попробуйте и посмотрите, не устранит ли проблема, если я не посмотрю в нее больше.

Редактировать

Вы на самом деле не нужен анализатор. Я думаю, что должен решить:

from django.core.files import ContentFile 

class Overlay(models.Model): 
    ... 

    def generate_sample(self): 
     base_pic = Image(filename=os.path.join(settings.BASE_DIR, 'girl.jpg')) 
     overlay_pic = Image(file=self.source) 
     result_pic = io.BytesIO() 

     if self.width or self.height: 
      resize_args = {} 
      if self.width: 
       resize_args['width'] = self.width 
      if self.height: 
       resize_args['height'] = self.height 
      overlay_pic.resize(**resize_args) 
     base_pic.composite(overlay_pic, self.px, self.py) 
     base_pic.save(file=result_pic) 

     content = result_pic.getvalue() 
     self.sample.save(self.name, ContentFile(content), False) 
     result_pic.close() 
     base_pic.close() 
     overlay_pic.close() 

Существует одна вещь, которая может быть потенциальной проблемой, он будет выполнять эту операцию каждый раз, когда Overlay модель сохраняется, даже если исходные изображения одинаковы. Но если он сохраняется редко, это не должно быть проблемой.

+0

судимое с «из django.core.files импорта файла» и «из django.core.files.images импорта ImageFile» и попробовал много других способов, и ничего, кажется, бывает. Тысячи заранее спасибо – netomo

+0

также попыталось с: pil_result_pic = pil_parser.close() django_file = File (открытым (pil_result_pic)) self.sample.save (self.name, django_file, False) А теперь получает следующее ошибка: принуждение к Unicode: нужна строка или буфер, найденный экземпляр. Я не знаю, но почему-то я думаю, что я ближе @ alexey-kuleshevich – netomo

+0

Я думаю, что привязка к Unicode происходит потому, что он не был открыт в двоичном режиме, но я не уверен на 100%. В любом случае, дайте мне знать, будет ли новое предлагаемое решение работать для вас. –

1

Только в случае, здесь более элегантная (на мой взгляд) реализация. Прежде всего, для этого требуется следующее приложение: django-smartfields.Как это решение лучше:

  • Он обновляет sample поле только тогда, когда source поле изменилось, и только прямо перед сохранением модели.
  • если keep_orphans опущено, старые source файлы будут очищены.

Фактический код:

import os 
from django.conf import settings 
from django.db import models 
from django.utils import six 

from smartfields import fields 
from smartfields.dependencies import FileDependency 
from smartfields.processors import WandImageProcessor 
from wand.image import Image 

class CustomImageProcessor(WandImageProcessor): 

    def resize(self, image, scale=None, instance=None, **kwargs): 
     scale = {'width': instance.width, 'height': instance.height} 
     return super(CustomImageProcessor, self).resize(
      image, scale=scale, instance=instance, **kwargs) 

    def convert(self, image, instance=None, **kwargs): 
     base_pic = Image(filename=os.path.join(settings.BASE_DIR, 'girl.jpg')) 
     base_pic.composite(image, instance.px, instance.py) 
     stream_out = super(CustomImageProcessor, self).convert(
      image, instance=instance, **kwargs): 
     if stream_out is None: 
      stream_out = six.BytesIO() 
      base_pic.save(file=stream_out) 
     return stream_out   


class Overlay(models.Model): 
    RELATIVE_POSITIONS = (...) 
    SIZE_MODES = (...) 

    name = models.CharField(max_length=50) 
    source = fields.ImageField(upload_to='overlays/%Y/%m/%d', dependencies=[ 
     FileDependency(attname='sample', processor=CustomImageProcessor()) 
    ], keep_orphans=True) 
    sample = models.ImageField(upload_to='overlay_samples/%Y/%m/%d', blank=True) 
    px = models.SmallIntegerField(default=0) 
    py = models.SmallIntegerField(default=0) 
    position = models.CharField(max_length=2, choices=RELATIVE_POSITIONS) 
    width = models.SmallIntegerField(default=0) 
    height = models.SmallIntegerField(default=0) 
    size_mode = models.CharField(max_length=1, choices=SIZE_MODES, default='B') 
    last_edit = models.DateTimeField(auto_now=True) 
+0

cool, дайте мне знать, если у вас возникнут какие-либо вопросы об этом решении или приложении smartfields в целом. –