전체 페이지뷰

2017년 1월 16일 월요일

Blog photo앱 : Pillow Part.1

블로그에 사진을 올리고 앨범으로 만들어 볼 수 있게 하는 기능을 만들어 볼 예정입니다.
전에 설치한 Pillow 라이브러리를 이용할 것입니다.

설계

화면 UI설계

사진과 관련된 화면은 세 개입니다.
처음 "photo"항목이 선택되었을 때 앨범리스트와 앨범이름, 앨범 설명을 나타내주는 화면.
앨범을 선택했을 때 앨범 내의 사진을 보여주는 화면,
그리고 사진을 선택했을 때 나타나는 화면입니다.
(자세한 내용은 파이썬 웹프로그래밍: 실전편 도서 참고 바랍니다)

테이블 설계

Album, Photo 테이블 두 개가 필요합니다.

Album
필드명
타입
설명
id Integer 기본 키
name CharField(50) 앨범제목
description CharField(1000 앨범 한줄 설명

Photo
id integer 기본 키
album ForeignKey Album에 대한 외래 키
title CharField(50) 사진 제목
image ThumbnailImageField 원본 및 썸네일
description TextField 사진 설명
upload_date DateTimeField 사진 업로드 일시

URL설계

/photo/             AlbumLV(ListView)        album_list.html
/photo/album/       AlbumLV(ListView)        album_list.html
/photo/album/99/    AlbumDV(DetailView)      album_detail.html
/photo/photo/99/    PhotoDV(DetailView)      photo_detail.view

코딩

뼈대만들기

가상환경으로 들어가  포토앱을 만듭니다.

(myvenv) D:\myDjango>python manage.py startapp photo

그런 후 포토앱을 settings.py에 등록합니다.

(myvenv) D:\myDjango\mysite>subl settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bookmark.apps.BookmarkConfig',
    'blog.apps.BlogConfig',  
    'tagging.apps.TaggingConfig'
    'disqus',   
    'django.contrib.sites',    
    'photo.apps.PhotoConfig'  #추가
]
cs

사진 업로드 기능을 위해, 사진 url요청을 인식할 수 있도록 다음의 두 항목도 등록되어야 합니다(우리는 이미 전에 등록해 두었습니다).

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
cs

모델 코딩

models.py

(myvenv) D:\myDjango\photo>subl models.py

from __future__ import unicode_literals
from django.utils.encoding import python_2_unicode_compatible
from django.db import models
from django.core.urlresolvers import reverse
from photo.fields import ThumbnailImageField
# Create your models here.
@python_2_unicode_compatible
class Album(models.Model):
    name = models.CharField(max_length=50)
    description = models.CharField('One Line Description', max_length=100, blank=True)
    class Meta:
        ordering = ['name']
    def __str__(self):
        return self.name
    def get_absolute_url(self):
        return reverse('photo:album-detail', args=(self.id,))
@python_2_unicode_compatible
class Photo(models.Model):
    album = models.ForeignKey(Album)
    title = models.CharField(max_length=50)
    image = ThumbnailImageField(upload_to='photo/%Y/%m')
    description = models.TextField('Photo Description', blank=True)
    upload_date = models.DateTimeField('Upload Date', auto_now_add=True)
    class Meta:
        ordering = ['title']
    def __str__(self):
        return self.title
    def get_absolute_url(self):
        return reverse('photo:photo_detail', args=(self.id,))
cs

사진에 대한 원본 이미지와 썸네일을 저장할 수 있는 직접 만든 커스텀 필드(장고가 제공하지 않는)인 ThumbnailImageField를 임포트 합니다.
Album과 Photo의 두 모델이 정의 되어 있습니다.
앨범에서는 name, description의 두 컬럼이 정의되어 있고, Meta 함수에서 정렬기준을 지정합니다.
포토에서는 album, title, image, description, upload_date의 다섯가지 컬럼이 정의됩니다.


admin.py

model을 admin.py에 등록합니다. 그리고, Admin에 AlbumAdimn과 PhotoAdmin을 추가로 만들어줍니다.

(myvenv) D:\myDjango\photo>subl admin.py

from django.contrib import admin
from photo.models import Album, Photo
# Register your models here.
class PhotoInline(admin.StackedInline):
    model = Photo
    extra = 2
class AlbumAdmin(admin.ModelAdmin):
    inlines = [PhotoInline]
    list_display = ('name''description')
class PhotoAdmin(admin.ModelAdmin):
    list_display = ('title''upload_date')
admin.site.register(Album, AlbumAdmin)
admin.site.register(Photo, PhotoAdmin)
cs

PhotoInline 클래스는 사진을 보여주는 방식을 정의하는 클래스입니다.
보여 주는 형식은 StackedInline과 TabularInline의 두가지가 있는데 전자는 세로, 후자는 테이블 모양으로 보여주는 방식입니다.

fields.py

커스텀 필드를 만들때 통상적으로 fields.py라는 이름을 사용합니다. 앞에서 미리 소개한 ThumbnailImageField를 만들어줍니다.

(myvenv) D:\myDjango\photo>subl fields.py

from django.db.models.fields.files import ImageField,ImageFieldFile
from PIL import Image
import os
def _add_thumb(s):
    parts = s.split(".")
    parts.insert(-1"thumb")
    if parts[-1].lower() not in ['jpeg','jpg']:
        parts[-1]='jpg'
    return ".".join(parts)
class ThumbnailImageFieldFile(ImageFieldFile):
    def _get_thumb_path(self):
        return _add_thumb(self.path)
    thumb_path = property(_get_thumb_path)
    def _get_thumb_url(self):
        return _add_thumb(self.url)
    thumb_url = property(_get_thumb_url)
    def save(self, name, content, save=True):
        super(ThumbnailImageFieldFile, self).save(name,content,save)
        img = Image.open(self.path)
        size = (128,128)
        img.thumbnail(size, Image.ANTIALIAS)
        background = Image.new('RGBA', size, (255,255,255,0))
        background.paste( img, (int((size[0]-img.size[0])/2), int((size[1]-img.size[1])/2) ))
        background.save(self.thumb_path, 'JPEG')
    def delete(self, save=True):
        if os.path.exists(self.thumb_path):
            os.remove(self.thumb_path)
        super(ThumbnailImageFieldFile, self).delete(save)
class ThumbnailImageField(ImageField):
    attr_class = ThumbnailImageFieldFile
    def __init__(self, thumb_width=128, thumb_height=128*args, **kwargs):
        self.thumb_width = thumb_width
        self.thumb_height = thumb_height
        super(ThumbnailImageField, self).__init__(*args, **kwargs)
cs


_add_thumb 함수는 썸네일 이미지를 만들어줍니다. 기존 이미지 이름과 호가장자 사이에 .thumb을 덧붙여 주는 식입니다. 예를 들어 a.jpg 라면 a.thumb.jpg로 썸네일을 만들어줍니다.

시스템에 직접 이미지를 쓰고 지우는 역할을 하는 ThumbnailImageFieldFile 클래스는 원본 파일의 경로를 처리해 썸네일의 경로를 만들고, 썸네일 이미지를 만들어 저장하는 기능을 가지고 있습니다.

ThumbnailImageField가 모델정의에 필요한 필드 역할을 합니다.
여기서는 커스컴으로 직접 필드를 만들어 썼지만 sort-thumbnail, easy-thumbnails, image-kit 등의 패키지를 이용하면 좀 더 간단하게 썸네일을 처리할 수 있다고 합니다.

이제 테이블이 변경되었으므로 DB에 반영해봅니다.

(myvenv) D:\myDjango>python manage.py makemigrations
Migrations for 'photo':
  photo\migrations\0001_initial.py:
    - Create model Album
    - Create model Photo

(myvenv) D:\myDjango>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, blog, bookmark, contenttypes, photo, sessions, sites, tagging
Running migrations:
  Applying photo.0001_initial... OK

잘 작동하는지 확인해 봅니다.

Admin에 접속해보면

포토 관련 테이블이 나타납니다. 각각 +Add를 눌러 확인해봅니다.


작업한 사항이 잘 반영되었습니다.

댓글 없음:

댓글 쓰기