Sunday, September 23, 2007

django で サムネイル付き画像をアップロードする方法(2) アップロードする方法2(modelを使う)

前回のサムネイル画像のアップロードの書き込み
http://programing-a2c-jp.blogspot.com/2007/09/django.html
ではviewの側にサムネイル画像の保存処理を書いていたがadminから画像を保存するときにサムネイルを自ら指定しないと保存できないといった問題があったのでmodelのsaveメソッドを再定義してサムネイル保存の処理を追加してみた。これでadmin側から画像を保存する時もサムネイルが自動生成されるようになった。

####### models.py class PhotoWithThumb ######
class PhotoWithThumb (models.Model):
photo_name = models.CharField(maxlength=200)
pub_date = models.DateTimeField('date published')
image = models.ImageField(upload_to='./', blank=False, null=True)
thumb = models.ImageField(upload_to='./', blank=True, null=True)

class Admin:
list_display = ('photo_name', 'pub_date', 'image', 'thumb')

def save(self):
thumbsize = settings.THUMBNAIL_FRAME_SIZE

# create file name and etc...
filename = os.path.join(settings.MEDIA_ROOT, self.get_image_filename())
filename = filename.encode()
try:
thumb_im = Image.open(filename)
except IOError:
# can't open original file...
sys.stderr.write("can't open original image to create thumbnail file\n")
raise IOError

#thumb_im = thumb_im.resize(thumbsize)
thumb_im = image_util.image_resize_keep_aspect( thumb_im, thumbsize )

body,ext = os.path.splitext( os.path.basename(filename) )
filename = body + "_thumb" + ".jpg"

# If the filename already exists, keep adding an underscore to the name of
# the file until the filename doesn't exist.
# took from django/build/lib/django/db/models/base.py
while os.path.exists(filename):
try:
dot_index = filename.rindex('.')
except ValueError: # filename has no dot
filename += '_'
else:
filename = filename[:dot_index] + '_' + filename[dot_index:]

thumb_im.save(os.path.join(settings.MEDIA_ROOT, filename), "JPEG")

# saveメソッドの中ではオブジェクトの __repr__ しか見ていないので次の書き方でよい。
self.thumb = filename

#以下のように書いてもよい。
"""
class Thumb:
def __init__(self, filename):
self.filename = filename
def __repr__(self):
return self.filename

self.thumb = Thumb(filename)
setattr(self.thumb, "filename", filename)
"""
models.Model.save(self)
###############################################################

view 側では PhotoWithThumb の photo_name と image, のフィールドに値を設定した後 save_image_fileメソッド を呼ぶ

############### views.py ############
def upload_photo_prompt(request):
"""
upload file and save with thumbnail file

thumbnail file name create as forrow ( original file name is 'orgfile.jpg')
'orgfile_thumb.jpg'

"""
thumbsize = settings.THUMBNAIL_FRAME_SIZE
t = loader.get_template('photos/upload_photo.html')

if request.method == 'POST':
# get cleaned PhotoWithThumbForm
form = PhotoWithThumbForm(request.POST, request.FILES)

if form.is_valid():
form.full_clean()
cleaned_data = form.cleaned_data

# get cleaned datetime
df = forms.DateTimeField()
cleaned_datetime = df.clean(datetime.datetime.now())

# stuff and save PhotoWithThumb
p = PhotoWithThumb()
p.photo_name = cleaned_data['photo_name']
p.image = cleaned_data['image']
p.pub_date = cleaned_datetime

p.save_image_file(cleaned_data['image'].filename, cleaned_data['image'].content)

return HttpResponseRedirect('../upload_photo_done/')
else:
form = PhotoWithThumbForm()

# display upload form
c = Context({ 'form' : form })
return HttpResponse(t.render(c))

########### image_util.py ##############
def image_resize_keep_aspect ( im, frame_size ):
"""
image resize but keep original aspect
@param im : source image
@param frame_size : maximam rectangle of image
"""

size = [0,0]
im_aspect = im.size[0] / float( im.size[1] )
frame_aspect = frame_size[0] / float( frame_size[1] )

if im_aspect > frame_aspect:
# fit image to frame width
size[0] = int( frame_size[0] )
size[1] = int( size[0] / im_aspect )
else:
# fit image to frame height
size[1] = int( frame_size[1] )
size[0] = int( size[1] * im_aspect )

return im.resize( size )

Wednesday, September 19, 2007

django の svn版をインストールする方法

svn版のインストールといってもcで書かれた部分がないので
$ python ./setup.py install --prefix=/YOUR_DJANGO_PATH/
とするだけ。
その手順をまとめてみた。

# 最初にディストリビューションのパッケージでインストールしているdjangoがあれば消しておく
# debian とかを使っている場合
$ sudo aptitude remove python-django

# svn 版のメインラインが使いたければ下記のURLからとってくる
$ svn checkout http://code.djangoproject.com/svn/django/trunk

# インストールするディレクトリを作る(NEW の所には日付とかを好みで使う)
$ mkdir -p /usr/local/django/django-svn-NEW

$ cd trunk

# インストール
$ python ./setup.py install --prefix=/usr/local/django/django-svn-NEW

# 次のアップデート時にパス名を変えないで済むようにdjango-svn-NEWに対するリンクを作成
$ sudo ln -s /usr/local/django/django-svn-NEW /usr/local/django/django

# パスを設定する(起動時に自動で設定されるように /etc/bash.bashrc などに書いておく)
$ export PATH=/usr/local/django/django/bin:$PATH
$ export \
PYTHONPATH=/usr/local/django/django/lib/python2.4/site-packages:$PYTHONPATH

これでインストール完了、次からアップデートするときは trunk のディレクトリで
$ svn up
としたあと新たなインストールディレクトリを作り、そこにインストール後リンクを変更すればOK(うまく動かなくなった時のために一つ前のバージョンぐらいはとっておく)

Tuesday, September 18, 2007

django で サムネイル付き画像を
アップロードする方法

djangoのsvn版(2007-09-16)のnewformsを使ってview側で主に処理を行うことでサムネイル付き画像のアップロードに挑戦してみた。
ファイルの保存を行うときはsaveメソッドでなくsave_FIELD_fileメソッド(FIELDには ファイルインスタンスのフィールド名が入る)を使うとかが、注意点かもしれない。

参考にしたのは
http://www.oluyede.org/blog/2007/03/18/django-image-uploading-validation-and-newforms/
など。
でも上記のサイトのコードにある

       
new_data = request.POST.copy()
new_data.update(request.FILES)
などはうまくいかなかったので含めなかった。
また、0.96 には無いnewformsのImageField()を使っている。

########### models.py ##########
from django.db import models
from django.db.models import fields
from django import newforms as forms

from django.utils.translation import gettext as _
# サムネイル画像データも扱うクラス'PhotoWithThumb'
class PhotoWithThumb (models.Model):
photo_name = models.CharField(maxlength=200)
pub_date = models.DateTimeField('date published')
image = models.ImageField(upload_to='./', blank=False, null=True)
thumb = models.ImageField(upload_to='./', blank=True, null=True)

class Admin:
list_display = ('photo_name', 'pub_date', 'image', 'thumb')
#ユーザからの入力を扱うクラス'PhotoWithThumbForm'
class PhotoWithThumbForm(forms.Form):
photo_name = forms.CharField(max_length=200, required=True, label=_('Photo Name'))
image = forms.ImageField(widget=forms.FileInput, required=True, label=_('Photo') )

########## views.py ############
# サムネイルを生成し、保存する処理の中心
from django.template import Context, loader
from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django import newforms as forms
from django.db.models import fields

import datetime
import Image
from StringIO import StringIO
from os import path

def upload_photo_prompt(request):

"""
upload file and save with thumbnail file

thumbnail file name create as forrow ( original file name is 'orgfile.jpg')
'orgfile_thumb.jpg'

"""
thumbsize = (255,255)
t = loader.get_template('photos/upload_photo.html')

if request.method == 'POST':
# get cleaned PhotoWithThumbForm
form = PhotoWithThumbForm(request.POST, request.FILES)

if form.is_valid():
form.full_clean()
cleaned_data = form.cleaned_data

# get cleaned datetime
df = forms.DateTimeField()
cleaned_datetime = df.clean(datetime.datetime.now())

# get cleaned thumbnail data
thumb_file = StringIO()

thumb_fname = cleaned_data['image'].filename
body,ext = path.splitext(path.basename(thumb_fname))
thumb_fname = body + "_thumb" + ".jpg"

im = Image.open(StringIO(cleaned_data['image'].content))
im = im.resize(thumbsize)
im.save(thumb_file, "JPEG")

thumb_data = { 'filename' : thumb_fname, 'content' : thumb_file.getvalue() }
thumb_field = forms.ImageField()
cleaned_thumb = thumb_field.clean(thumb_data)

# stuff and save PhotoWithThumb
p = PhotoWithThumb()
p.photo_name = cleaned_data['photo_name']
p.image = cleaned_data['image']
p.pub_date = cleaned_datetime
p.thumb = cleaned_thumb

p.save_image_file(cleaned_data['image'].filename, cleaned_data['image'].content)
p.save_thumb_file(cleaned_thumb.filename, cleaned_thumb.content)

return HttpResponseRedirect('../upload_photo_done/')
else:
form = PhotoWithThumbForm()

# display upload form
c = Context({ 'form': form })
return HttpResponse(t.render(c))

########## upload_photo.html ################
{% extends "plane.html" %}

{% block title %} Upload image {% endblock %}

{% block body_content %}
<h1> Upload image </h1>
<form method="post" enctype="multipart/form-data">
<table>
{{ form }}
</table>
<input type="submit" value="upload photo"/>
</FORM>
{% endblock %}


######### upload_photo_done.html #########
{% extends "plane.html" %}

{% block title %} Upload image {% endblock %}

{% block body_content %}
<h1> image is uploaded </h1>
{% endblock %}


########## plane.html #########
<!DO
CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<HTML xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<HEAD>
<TITLE>{% block title %} Plane html page {% endblock %}</TITLE>
</HEAD>
<BODY>
{% block body_content %} {% endblock %}
</BODY>
</HTML>

以上のような形でサムネイル画像を自動で保存できるようになったが、views.pl の側で処理を行っていると admin で写真をアップロードした場合に自動でサムネイル画像を生成&保存してくれないなど問題も多いので今後modelのsave_FIELD_fileメソッドをオーバーライドすることでサムネイルの保存もできるようにしたい。