2009-07-22 75 views
11

Soy nuevo en Django (y en Python) y he estado tratando de resolver algunos detalles antes de utilizar otras aplicaciones. Tengo problemas para entender dónde encajan las cosas en la forma de hacer las cosas de Django (o Python). Lo que estoy tratando de resolver es cómo cambiar el tamaño de una imagen, una vez que ha sido cargada. Tengo mi configuración modelo muy bien y enchufado a la administración, y la imagen UPLOADS bien en el directorio:¿Cambiar el tamaño de la imagen con django?

from django.db import models 

# This is to list all the countries 
# For starters though, this will be just United Kingdom (GB) 
class Country(models.Model): 
    name = models.CharField(max_length=120, help_text="Full name of country") 
    code = models.CharField(max_length=2, help_text="This is the ISO 3166 2-letter country code (see: http://www.theodora.com/country_digraphs.html)") 
    flag = models.ImageField(upload_to="images/uploaded/country/", max_length=150, help_text="The flag image of the country.", blank=True) 

    class Meta: 
     verbose_name_plural = "Countries" 

    def __unicode__(self): 
     return self.name 

Lo que ahora estoy teniendo problemas con que se toma ese archivo y hacer un nuevo archivo en una imagen en miniatura. Como digo, me gustaría saber cómo hacerlo sin usar las aplicaciones de los demás (por ahora). Tengo este código de DjangoSnippets:

from PIL import Image 
import os.path 
import StringIO 

def thumbnail(filename, size=(50, 50), output_filename=None): 
    image = Image.open(filename) 
    if image.mode not in ('L', 'RGB'): 
     image = image.convert('RGB') 
    image = image.resize(size, Image.ANTIALIAS) 

    # get the thumbnail data in memory. 
    if not output_filename: 
     output_filename = get_default_thumbnail_filename(filename) 
    image.save(output_filename, image.format) 
    return output_filename 

def thumbnail_string(buf, size=(50, 50)): 
    f = StringIO.StringIO(buf) 
    image = Image.open(f) 
    if image.mode not in ('L', 'RGB'): 
     image = image.convert('RGB') 
    image = image.resize(size, Image.ANTIALIAS) 
    o = StringIO.StringIO() 
    image.save(o, "JPEG") 
    return o.getvalue() 

def get_default_thumbnail_filename(filename): 
    path, ext = os.path.splitext(filename) 
    return path + '.thumb.jpg' 

... pero esto en última instancia, me ha confundido ... Como no sé cómo esta 'encaja' Django a mi aplicación? Y realmente, ¿es la mejor solución para simplemente hacer una miniatura de una imagen que se ha cargado correctamente? ¿Alguien puede mostrarme una buena, sólida y decente manera que un principiante como yo puede aprender a hacer esto correctamente? Como en, saber dónde poner ese tipo de código (models.py? Forms.py? ...) y cómo funcionaría en contexto? ... Solo necesito un poco de ayuda para entender y resolver este problema.

¡Gracias!

Respuesta

7

Si está bien para ti, hay una aplicación Django listo, haciendo exactamente lo que quiere: https://github.com/sorl/sorl-thumbnail

+2

Gracias leafnode, me He oído hablar de esto y lo voy a ver. Pero esperaba tratar de comprender las formas subyacentes de hacer las cosas primero para poder entender mejor cómo funciona todo ... ¿Tal vez no vale la pena hacerlo? ... pero estaba pensando que valió la pena hacerlo para convertirme en un mejor desarrollador de Python/Django? – littlejim84

+0

Si desea entrar en detalles, mire la otra respuesta, pero recuerde que es para formularios del lado del usuario. – leafnode

+1

es deaaaaaaaaaaaaad – titus

2

No estoy seguro sobre el código que envió, porque nunca utilizar modelos como tal, pero hay otro método .

Puede implementar su propio FileUploadHandler para manejar las cargas de archivos de imágenes. El ejemplo es here. Justo después de la línea 37 (dest.close()) use la función thumbnail(upload_dir + upload.name) (la que ha enviado).

Espero que te ayude.

2

Una pregunta clave es: cuando se debe generar la miniatura?

  1. ¿Dinámicamente cuando el usuario solicita una imagen en miniatura?
  2. O desea crear un archivo de disco físico cada vez que un país se INSERTE/ACTUALICE en la base de datos.

If (1) Te sugiero que crees una vista que se corresponda con la url /flagthumbnail/countryid. El método vista tendría entonces que:

  1. obtener la instancia país de la base de datos de
  2. leer la imagen de la bandera en un PIL imagen y cambiar el tamaño de eso.
  3. Crea (y devuelve) una HTTPResponse con el tipo de contenido correcto y escribe la imagen de PIL en la respuesta.

Cuando necesite mostrar una bandera de miniatura, simplemente use <a href="/flagthumbnail/countryid">.

Si (2), puede conectarse a la señal django.db.models.signals.post_save de Django y en el controlador de señales cree y guarde un archivo de miniaturas.

2

Supongo que depende de cómo y cuándo use las miniaturas.

Si desea crear algunas miniaturas cada vez que se guarda el País, que podría hacerlo de esta manera:

from django.db import models 

# This is to list all the countries 
# For starters though, this will be just United Kingdom (GB) 
class Country(models.Model): 
    name = models.CharField(max_length=120, help_text="Full name of country") 
    code = models.CharField(max_length=2, help_text="This is the ISO 3166 2-letter country code (see: http://www.theodora.com/country_digraphs.html)") 
    flag = models.ImageField(upload_to="images/uploaded/country/", max_length=150, help_text="The flag image of the country.", blank=True) 

    class Meta: 
     verbose_name_plural = "Countries" 

    def __unicode__(self): 
     return self.name 

    def save(self, force_insert=False, force_update=False): 
     resize_image(self.flag) 
     super(Country, self).save(force_insert, force_update) 

Si no está 100% seguro de qué tamaños usted necesitará sus imágenes, podría cambiar el tamaño de ellos en el último minuto. Lo he visto de manera efectiva con una plantilla (creo en una versión en Pinax). Usted crea una etiqueta de plantilla que toma la imagen y un tamaño, luego crea y guarda la imagen del tamaño apropiado si es necesario, o muestra una imagen creada previamente si está allí. Funciona bastante bien

+0

El código anterior que me diste aquí es más de lo que buscaba ... Así que cuando se guarda el modelo, el archivo se carga, se guarda y la miniatura se crea y guarda también. Cambiará tan poco, que esto parece tener más sentido. Entonces con eso, ¿dónde mantendría la función resize_image()? En un archivo llamado resize_image.py? ¿Y dónde pondría realmente ese archivo y lo importaría? Dentro de mi proyecto Django? O en mi sitio-paquetes o algo así? (Aquí también es donde estoy teniendo un poco de confusión) – littlejim84

+0

... también ... he visto esto antes con el tipo de código que ponga para arriba ... def Guardar (auto, * args, ** kwargs): que he visto este args * y ** kwargs un par de veces ? Qué significa eso? ¿Tiene algún efecto si los dejo entrar o salir, etc.? – littlejim84

+0

@ littlejim84: ha pasado un tiempo desde que lo pidió ... pero, su respuesta: args a menudo representa los argumentos sin nombre para la función, y los kwargs serían los argumentos nombrados (por ejemplo, my_func (arg1, arg2, namearg1 = 'dd', namearg2 = 'ddd') ..... luego en my_func (self, * args, ** kwargs), args = [arg1, arg2], kwargs = {'namearg1': 'dd', 'namearg2': 'ddd'} ... es útil pasarlos a la función que se sobrescribe para que no se rompa ninguna funcionalidad en la función original. –

4

Esto es lo que uso en mis modelos para guardar una nueva miniatura si la imagen cargada ha cambiado. Está basado en otro DjangoSnippet pero no recuerdo quién escribió el original. Si sabes, agrega un comentario para que pueda darles crédito.

from PIL import Image 
from django.db import models 
from django.contrib.auth.models import User 

import os 
import settings 

class Photo_Ex(models.Model): 
    user = models.ForeignKey(User, blank=True, null=True)  
    photo = models.ImageField(upload_to='photos') 
    thumbnail = models.ImageField(upload_to='profile_thumb', blank=True, 
           null=True, editable=False) 

    def save(self, *args, **kwargs): 
     size = (256,256) 
     if not self.id and not self.photo: 
      return 

     try: 
      old_obj = Photo_Ex.objects.get(pk=self.pk) 
      old_path = old_obj.photo.path 
     except: 
      pass 

     thumb_update = False 
     if self.thumbnail: 
      try: 
       statinfo1 = os.stat(self.photo.path) 
       statinfo2 = os.stat(self.thumbnail.path) 
       if statinfo1 > statinfo2: 
        thumb_update = True 
      except: 
       thumb_update = True 

     pw = self.photo.width 
     ph = self.photo.height 
     nw = size[0] 
     nh = size[1] 

     if self.photo and not self.thumbnail or thumb_update: 
      # only do this if the image needs resizing 
      if (pw, ph) != (nw, nh): 
       filename = str(self.photo.path) 
       image = Image.open(filename) 
       pr = float(pw)/float(ph) 
       nr = float(nw)/float(nh) 

       if image.mode not in ('L', 'RGB'): 
        image = image.convert('RGB') 

       if pr > nr: 
        # photo aspect is wider than destination ratio 
        tw = int(round(nh * pr)) 
        image = image.resize((tw, nh), Image.ANTIALIAS) 
        l = int(round((tw - nw)/2.0)) 
        image = image.crop((l, 0, l + nw, nh)) 
       elif pr < nr: 
        # photo aspect is taller than destination ratio 
        th = int(round(nw/pr)) 
        image = image.resize((nw, th), Image.ANTIALIAS) 
        t = int(round((th - nh)/2.0)) 
        image = image.crop((0, t, nw, t + nh)) 
       else: 
        # photo aspect matches the destination ratio 
        image = image.resize(size, Image.ANTIALIAS) 

      image.save(self.get_thumbnail_path()) 
      (a, b) = os.path.split(self.photo.name) 
      self.thumbnail = a + '/thumbs/' + b 
      super(Photo_Ex, self).save() 
      try: 
       os.remove(old_path) 
       os.remove(self.get_old_thumbnail_path(old_path)) 
      except: 
       pass 

    def get_thumbnail_path(self): 
     (head, tail) = os.path.split(self.photo.path) 
     if not os.path.isdir(head + '/thumbs'): 
      os.mkdir(head + '/thumbs') 
     return head + '/thumbs/' + tail 

    def get_old_thumbnail_path(self, old_photo_path): 
     (head, tail) = os.path.split(old_photo_path) 
     return head + '/thumbs/' + tail 
+3

Hay una función 'image.thumbnail (size)' que hace esto por usted, en realidad no lo hace es necesario calcular el ancho/alto/proporción, etc. –

2

Sustitución del método ahorra es una buena opción, pero estaría más tentado a usar un signal en este caso. Las señales de Django le permiten "escuchar" varios eventos de un tipo de modelo determinado; en este caso, le interesaría el evento post_save.

Normalmente me suscribo a tales señales en mi archivo models.py. Código para usted sería algo como esto:

from django.db.models.signals import post_save 
from models import Country 

def resize_image(sender, **kwargs): 
    country = kwargs["instance"] 
    resize_image(country.flag) # where resize_image generates a thumbnail given a Country instance 

post_save.connect(resize_image, sender=Country) 
+1

Esto es un poco confuso, ¿no? Quizás cambiar el nombre de la función de redimensionar llamada? – justin

2

Ryan es señales correctas son una mejor manera de ir sin embargo, la ventaja de la overriden ahorra es que podemos obtener las rutas de imágenes antiguas y nuevas, a ver si la imagen tiene cambiado (y si ha creado una nueva miniatura), guarde la instancia del modelo y luego elimine la imagen y la miniatura antiguas.

Recuerde que django no limpia las imágenes antiguas por lo que a menos que tenga una secuencia de comandos para verificar que las imágenes/miniaturas todavía estén en uso y limpie las que no sean, perderá espacio en el disco. (Esto puede o no puede ser un problema para usted dependiendo del tamaño de la imagen y la frecuencia de las actualizaciones)

No estoy seguro de cómo podría hacer esto con una señal de post_save, y no sé lo suficiente sobre las señales (¡Eso es una investigación para esta noche!) Para saber si hay una señal pre_save adecuada. Si encuentro uno, volveré a escribir el código anterior para usar las señales como un genérico prealmacenador.

+1

Sí, también hay una señal pre_save. Encuentro que las señales son generalmente más agradables para trabajar, pero hay Definitivamente son momentos en los que se prefiere salvar(). –

0

También juro por Justin Driscoll django-photologue también es ideal para cambiar el tamaño. It:

  1. Cambia el tamaño de (que se puede escalar a una altura o anchura proporcionalmente)
  2. Cultivos (tipo-de forma inteligente: desde el centro, arriba, izquierda, abajo o derecha)
  3. Opcionalmente upsizes
  4. se puede agregar efectos (como "color", "brillo", "contraste" y "nitidez", así como filtros como "Hallar bordes" y "relieve". nitidez de imágenes. Haga tu miniaturas en blanco y negro.)
  5. se puede agregar sencilla marca de agua
  6. caché los resultados

Básicamente es impresionante.

2

le puede dar una oportunidad a:

image headcut

características:

  • Le permite configurar el centro de atención de una imagen ... cabezas no se cortan nunca más. imagen en varios sitios web que une
  • Configuración sencilla y su uso
  • vídeo thumbnailing
  • Evita
1

Otra alternativa es utilizar Imagemagick directamente, si se quiere evitar una serie de dificultades con la almohada y Python 3 como this one .

from subprocess import call 
call(['convert', img_path_file_name, '-thumbnail', target_size_str, '-antialias', style_path_file_name]) 

Se podría llamar a este modelo en Guardar o en una etiqueta de plantilla para generar una copia de una sola vez manipulada del archivo original de una manera archivo de almacenamiento en caché, o incluso una tarea apio.

Puede obtener un ejemplo de cómo he utilizado esto en uno de mis proyectos:

Cuestiones relacionadas