Django Admin 图片上传与缩略图处理

终于用完全Django式的方法完成了Admin界面的图片上传与缩略图处理啰!

什么是“django式的方法”,意思就是都是利用Django的类和方法来实现的(除了缩略图处理以外)。因为没做任何其他扩展,所以一些步骤还是需要手动的,比如把图片粘贴进文本区域(-_-#)。

进一步完善后,可以做一个比较自动化的界面来写文章了(像WP一样)!

嗯!最近在完善相当早之前写的TXblog,打算用其来代替现在用的WordPress。

前天写到设计一个模型来存图片,要用ImageField和FilePathField。那天写完后经过反复思考,发现这样是不合适的。应该都用ImageField!

首先来说如何处理缩略图,很简单,用PIL库就可以完成。下面的函数即把给定位置的一张图片处理为480宽度的缩略图(如果小于480才进行处理)。

from __future__ import division
import os
import Image

def make_thumb(path, size = 480):
    pixbuf = Image.open(path)
    width, height = pixbuf.size

    if width > size:
        delta = width / size
        height = int(height / delta)
        pixbuf.thumbnail((size, height), Image.ANTIALIAS)

        return pixbuf

下面是我设计的Media模型,image为图片本身,thumb将在重写的save函数中生成。与Post(即文章)的关系是ForeignKey,即一张图片必对应一篇文章,而文章不一定包含图片。这样设计还能使用Admin的Inline功能与编辑文章的界面显示在一块。请看后面。

还要提提如何手动构建一个ImageField。与CharFiled、IntegerField这类简单的字段不同,CharFiled对应Python的unicode,IntegeField对应int,直接写就可以。而ImageField对应的是ImageFieldFile这个Django自定义的类,所以一定要用Django式的方式构建

ImageFieldFile继承于FieldFile,建立时需要对象本身,对应的字段和图片的相对路径。所以我就用下面的方法手动建立,并调用save来保存。

import os

from django.db import models
from settings import MEDIA_ROOT
from django.utils.translation import ugettext as _
from django.db.models.fields.files import ImageFieldFile
from utils import make_thumb
from pulog.models import Post

UPLOAD_ROOT = 'upload'
THUMB_ROOT = 'upload/thumb'

class Media(models.Model):
    title = models.CharField(max_length = 120)
    image = models.ImageField(upload_to = UPLOAD_ROOT)
    thumb = models.ImageField(upload_to = THUMB_ROOT, blank = True)
    date = models.DateTimeField(auto_now_add = True)
    post = models.ForeignKey(Post)

    class Meta:
        verbose_name_plural = _('Media')

    def save(self):
        base, ext = os.path.splitext(os.path.basename(self.image.path))
        thumb_pixbuf = make_thumb(os.path.join(MEDIA_ROOT, self.image.name))
        relate_thumb_path = os.path.join(THUMB_ROOT, base + '.thumb' + ext)
        thumb_path = os.path.join(MEDIA_ROOT, relate_thumb_path)
        thumb_pixbuf.save(thumb_path)
        self.thumb = ImageFieldFile(self, self.thumb, relate_thumb_path)
        super(Media, self).save()

    def __unicode__(self):
        return _('%s, uploaded at %s') % (self.title, self.date.strftime('%T %h %d, %Y'))

最后是Admin相关的,在Media这个App下建立一个admin.py,内容是:

from django.contrib import admin
from models import Media

class MediaAdmin(admin.StackedInline):
    model = Media
    admin.site.register(Media)

然后再把Post的admin.py处加一句inlines相关的字段:

from media.admin import MediaAdmin

class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'date', 'author')
    radio_fields = {'post_status': admin.VERTICAL,
                    'type': admin.VERTICAL,
                    'comment_status': admin.VERTICAL}
    inlines = [MediaAdmin,]

    class Media:
        js = ( 
            '/static/js/tiny_mce/tiny_mce.js',
            '/static/js/textareas.js',
        )   
        admin.site.register(Post, PostAdmin)

好了!这样的话,两个模型在一个页面里就可以操作了!相当于一边写文章,突然想加图片,那就直接加吧!根据Django Admin的丰富选项,还可以一次性加多张图片,默认是3格。