Django, imatges i Imagekit


Escrit per Aaloy a 23 de May , 2009 a les 4:46 p.m.

Django Imagekit és una llibreria creada per Justin Driscoll que ens permet crear miniatures i/o distints tamanys d'imatges a partir de la imatge original, i que s'integra molt bé amb Django. És una llibreria més senzilla que la de django-photologue ja que no té tota la funcionalitat per a crear gal·leries fotogràfiques.

El tutorial per a fer-la anar està força bé, però aprofitaré la benentesa per a fer cinc cèntims de com podem pujar una imatge al nostre site amb Django i presentar-la de nou.

El codi font de l'exemple complet és a appfusedjango.

El model

Per començar definim el model:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django.db import models
from imagekit.models import ImageModel

from django.db import models
from imagekit.specs import ImageSpec

class Photo (ImageModel):
    image = models.ImageField(upload_to='photos')
    comments = models.TextField()
    num_views = models.PositiveIntegerField(editable = False, default = 0)

    class IKOptions:
        spec_module = 'sample.specs'
        cache_dir = 'photos/cache'
        image_field = 'image'
        save_count_as = 'num_views'

    def thumb(self):
        if self.image:
            return '<img src="%s">' % self.thumbnail.url
        else:
            return ""
    thumb.allow_tags = True
    thumb.short_description = 'Foto'

En aquest cas es tracta d'un model molt senzill, guardam la imatge image al directori photos i posarem dins la base de dades tant la referència del fitxer (això és important, dins la base de dades no és guarda la imatges sinó sols la metadada) i el comentari.

El camp num_views ens pots servir per anar guardar la quantitat de vegades que s'ha vist la imatge i és utilitzat si volem per la llibreria d'Imagekit.

La part interessant és a la classe IKOptions. Aquesta defineix quin tractament se li donarà a la imatge, on s'enmagatzemaran les miniatures i a quin camp es fa referència.

  • spec_module És el mòdul d'ImageKit que es farà servir i que defineix els tamanys possibles. D'aquí una estona el veurem amb detall. En el nostre cas el modul es diu specs.
  • cache_dir : guardarem els distints tamanys generats a photos/cache
  • image_field: Les metadaes son pel camp image del model.

El mètode thumb ens serviex per poder posar la miniatura dins el llistat de l'admin.

Els tamanys

Per definir els tamanys Imagekit distingeix entre el que és la visualització de la imatge i les manipulacions que s'hi fan. La imatge original necessita passar per uns filtres que Imagekit anomena processors. Una imatge pot generar-se aplicant un o més d'aquests filtres.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from imagekit.specs import ImageSpec
from imagekit import processors

# define the thumnail processor
class ResizeThumb(processors.Resize):
    width = 100
    height = 75
    crop = True

class ResizeDisplay(processors.Resize):
    width = 600

class ResizeBig(processors.Resize):
    width = 800

# define your spec
class Thumbnail(ImageSpec):
    pre_cache = True
    processors = [ResizeThumb,]

class Display(ImageSpec):
    processors = [ResizeDisplay,]

class Big(ImageSpec):
    processors = [ResizeBig,]

A l'exemple sols faig servir un tipus de processor el de Resize per a redimensonar la imatge als tamanys que farem servir.

Pujam una imatge

Per pujar una imatge necessitam definir un formulari, el mètode que tractarà aquest formulari i les urls que farem servir, així com les plantilles que es mostraran.

Les urls

Aquesta és la part senzilla.

1
2
3
4
5
6
7
8
9
from django.conf.urls.defaults import *
from django.conf import settings 
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',    
     url(r'^$','sample.views.index', name="main-page"),
     url(r'^display/(?P<id>\d+)/$','sample.views.display', name="display-image"),
....

Definim una url per l'index que identificarem per nom main-page i que serà tractada al mètode index del mòdul views del nostre paquet sample.

I una altra ulr que ens permetrà visualitzar la imatge a partir del seu identificador. Anomenam a aquesta url display-image, original que és un...

El formulari

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# -*- coding: UTF-8 -*-

from django import forms
from PIL import Image

class AttachmentForm(forms.Form):
    """Form for the attachment sample. Added a simple validation
    to accept only png files checking for 'image/png' in the
    content_type of the file"""
    image = forms.FileField(help_text="add a png or jpg file")
    comments = forms.CharField(widget= forms.Textarea, help_text="describe the image")

    def clean(self):
        "Validate the entire form"
        cleaned = self.cleaned_data        
        try:
            file = cleaned['image']
        except Exception, e:
            # perhaps this is not a file
            raise forms.ValidationError("Not valid file: %s" % e)
        if not file.content_type.lower() in ["image/jpeg", "image/png", "image/jpg"]:            
            raise forms.ValidationError("Just jpg or png files please")
        im = Image.open(file)
        if not im.format in ['JPEG','PNG']:
            raise forms.ValidationError("Just jpg or png files please")
        return cleaned

El formulari com es pot veure és d'allò més normalet, definim un camp per la imatge i un camp per als comentaris.

La part "nova" està en la validació. No ens podem fiar del que ens diu la gent que puja, així que le que farem és comprovar que el mime type es correspon amb un format vàlid, i com que fins i tot això es pot manipular, farem una comprovació addicional amb PIL per a comprovar que la imatge és el que diu ser. Aquesta comprovació dependrà del vostre nivell de paranoia.

Tractant la imatge

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from models import Photo
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from forms import AttachmentForm

def index(request):
    "Obtains the attachment and saves it to the disk"
    if request.method == 'POST':
        form = AttachmentForm(request.POST, request.FILES)
        if form.is_valid():
            f = form.cleaned_data['image']
            foto = Photo()
            foto.image.save(f.name, f)
            foto.comments = form.cleaned_data['comments']
            foto.save()
            return HttpResponseRedirect('/')
    else:
        form = AttachmentForm()
    fotos = Photo.objects.all()
    return render_to_response('index.html', {'form':form, 'fotos': fotos})

Per a tractar arxius i imatges la part delicada és recordar que hem de passar la informació al formulari afegint el request.FILES i pensar a posar al formulari enctype="multipart/form-data"

A més d'això hem de guardar dues vegades: una per la imatge, que guardarà el contingut dins el sistema de fitxers, i una altra per la resta del model i les metadades de la imatge. Per això tenim un foto.image.save(f.nam, f) guarda la imatge al sistema de fitxers.

Mostrar una imatge és prou senzill

1
2
3
def display(request, id):
    foto = Photo.objects.get(pk=id)
    return render_to_response('image.html', {'foto': foto})

Mostrant les imatges

Al template hi passam objectes del tipus Photo, que recordem tenen tota la fontaneria del ImageKit.

Això vol dir que a més de la imatge original, puc fer servir els formats que he definit a specs: Thumbnail, Display, Big.

Per utilitzar-los en la nostra plana sols hi hem de fer referència:

1
2
3
<li><img src="{{foto.thumbnail.url}}" /></li>
<li><img src="{{foto.display.url}}" /></li>
<li><img src="{{foto.big.url}}" /></li>

com podem veure sols és cosa de fer referència al tamny definit i treure'n el que ens interessa, en el nostre cas la url per a mostrar la imatge.

Per darrera ImageKit se n'ha encarregat de fer les transformacions i guardar la imatge a la caché, de manera que la feina pesada de generació dels distints tamnays sols se fa un cop.

A partir d'aquí ens podem comlicar tant com voguem, per exemple:

  • Al mètode clean fer que no es puguin pujar imatges de més d'un tamany.
  • Reescriure el mètode save del model per a que no es guardi la imatge original sinó una altra imatges ja reduïda.
  • Escriure més processors per fer més manipulacions a les imatges.
  • etc. etc.

Però el és segur és que amb això que us he contat tingueu el 90% dels casos solucionats.

Nota: Oscar, esper que això et servesqui ;)


Enllaços citats
Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python Django


Comentaris

1 Comentari de oscar a les 10:05 del Sunday 24 May de 2009

debianBox:~/project/appfusedjango-read-only/thumbs_prj$ python manage.py runserver

Error: Can't find the file 'settings.py' in the directory containing 'manage.py'. It appears you've customized things.
You'll have to run django-admin.py, passing it your settings module.
(If the file settings.py does indeed exist, it's causing an ImportError somehow.)

Em dona aquest error a tots el subprojectes.

Edit: sembla que els comentaris no funcionen


2 Comentari de oscar a les 10:05 del Sunday 24 May de 2009

Edit: ara funcionen els comentaris :)

Merci per aquest article!!!!


3 Comentari de aaloy a les 11:05 del Sunday 24 May de 2009

Per a fer anar els exemples s'ha de copiar el properties.py.template a properties.py i configurar-ho segons com tenguis el teu sistema. En el 90% del casos amb la còpia bastarà.


Avís: Els comentaris es tanquen automàticament als 30 dies