2015-01-30 4 views
4

장고 모델에 저장되어있는 "오버레이"설정에 대한 미리보기를 생성하려고하는데 나중에 다른 모델에 적용됩니다. 내가하지 많은 경험 파이썬으로 파일 조작이 ... = (완드로 생성 된 이미지를 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에 파일을 저장할 때마다 장고의 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 = 파일 (오픈 (pil_result_pic)) self.sample.save (self.name, django_file, 거짓) 이제 점점 다음 오류 : 유니 코드로 강제 변환 : 필요한 문자열 또는 버퍼가 필요합니다. 인스턴스가 발견되었습니다. 모르겠지만 어쨌든 나는 @ @ alexey-kuleshevich 가까이 있다고 생각한다. – netomo

+0

유니 코드로의 coersing은 바이너리 모드로 열리지 않았기 때문에 일어난다 고 생각한다. 그러나 나는 100 % 확실하지 않다. 어떤 경우 든 제안 된 새로운 솔루션이 귀하에게 적합한 지 여부를 알려주십시오. –

1

더 우아한 (내 의견으로는) 구현. 우선이 앱이 필요합니다 : django-smartfields.이 솔루션은 더 나은 방법 :

  • 그것은 source 필드가 변경, 단지 바로 모델을 저장하기 전에 만하면 sample 필드를 업데이트합니다.
  • keep_orphans을 생략하면 이전 source 개의 파일이 정리됩니다.

실제 코드 :와 Tryed

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

멋진데,이 솔루션이나 스마트 필드 앱에 대해 궁금한 점이 있으면 알려 주시기 바랍니다. –

관련 문제