2009-07-22 25 views
11

Sono nuovo di Django (e Python) e ho cercato di elaborare alcune cose da solo, prima di utilizzare le app di altre persone. Ho difficoltà a capire dove le cose 'si adattano' al modo di fare Django (o Python). Quello che sto cercando di capire è come ridimensionare un'immagine, una volta che è stata caricata. Io ho la mia messa a punto del modello piacevolmente e collegato a l'amministratore, e l'immagine uploads multa alla directory:Ridimensionamento delle immagini 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 

La cosa ora sto avendo problemi con sta prendendo quel file e fare un nuovo file in una miniatura. Come ho detto, mi piacerebbe sapere come farlo senza usare le app degli altri (per ora). Io ho questo codice da 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' 

... ma questo mi ha in ultima analisi, confuso ... Come io non so come questo 'si adatta' al mio Django app? E davvero, è la soluzione migliore per fare semplicemente una miniatura di un'immagine che è stata caricata con successo? Qualcuno può mostrarmi un modo buono, solido e decente che un principiante come me possa imparare a farlo correttamente? Come in, sapendo dove mettere quel tipo di codice (models.py? Forms.py? ...) e come funzionerebbe nel contesto? ... Ho solo bisogno di un po 'di aiuto per capire e risolvere questo problema.

Grazie!

risposta

7

Se va bene per voi, c'è un applicazione Django pronto, facendo esattamente quello che vuoi: https://github.com/sorl/sorl-thumbnail

+2

Grazie leafnode, ho ne ho sentito parlare e lo guarderò. Ma speravo di provare a capire i modi sottostanti di fare le cose per prima cosa, spero di farmi capire meglio come funziona ... Forse non vale la pena farlo? ... ma stavo pensando che ne valesse la pena farlo per diventare uno sviluppatore Python/Django migliore? – littlejim84

+0

Se ti piace entrare nei dettagli, guarda l'altra risposta, ma ricorda che è per i moduli sul lato utente. – leafnode

+1

è deaaaaaaaaaaaaad – titus

2

Non sono sicuro circa il codice hai mandato, perché non ho mai utilizzare modelli in quanto tali, ma c'è un altro metodo .

È possibile implementare il proprio FileUploadHandler per la gestione dei caricamenti di file immagine. L'esempio è here. Subito dopo la linea 37 (dest.close()) usa la funzione thumbnail(upload_dir + upload.name) (quella che hai inviato).

Spero che ti aiuti.

2

Una domanda chiave è: quando deve essere generata la miniatura?

  1. Dinamicamente quando l'utente richiede un'immagine di anteprima?
  2. Oppure si desidera creare un file del disco fisico ogni volta che un paese è INSERISCI/AGGIORNATO nel database.

Se (1) suggerisco di creare una vista che mappa all'url /flagthumbnail/countryid. Il metodo di vista sarebbe quindi necessario:

  1. ottenere l'istanza Paese dal database
  2. Leggere l'immagine della bandiera in un PIL immagine e ridimensionare quello.
  3. Creare (e restituire) un HTTPResponse con il tipo di contenuto corretto e scrivere l'immagine PIL nella risposta.

Ogni volta che è necessario visualizzare un indicatore di anteprima, utilizzare solo <a href="/flagthumbnail/countryid">.

Se (2), è possibile connettersi al segnale django.db.models.signals.post_save di Django e nel gestore di segnale creare e salvare un file di anteprima.

2

Suppongo che dipenda da come e quando si utilizzano le anteprime.

Se si desidera creare alcune miniature ogni volta che il Paese si salva, si potrebbe fare in questo modo:

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) 

Se non si è sicuri al 100% che formati avrete bisogno le immagini, è potrebbe ridimensionarli all'ultimo minuto L'ho visto efficacemente fatto con un templatetag (credo in una versione su Pinax). Si crea un templatetag che prende l'immagine e una dimensione, quindi crea e salva l'immagine della dimensione appropriata se necessario, o visualizza uno precedentemente creato se è lì. Funziona piuttosto bene.

+0

Il codice sopra riportato che mi hai dato qui è più quello che cercavo ... Quindi, quando il modello viene salvato, il file viene caricato, salvato e la miniatura viene quindi creata e salvata. Cambierà così poco, che questo sembra avere più senso. Quindi con quello, dove manterrei la funzione resize_image()? In un file chiamato resize_image.py? E dove dovrei effettivamente mettere quel file e importarlo? All'interno del mio progetto Django? O nei miei pacchetti del sito o qualcosa del genere? (questo è anche dove sto avendo un po 'di confusione) – littlejim84

+0

... anche ... ho visto questo prima con il tipo di codice che mettere in su ... def salvare (auto, * args, ** kwargs): Ho visto questo * args e ** kwargs un paio di volte ? Cosa significa? Ha qualche effetto se li lascio dentro o fuori, ecc? – littlejim84

+0

@ littlejim84: da un po 'di tempo che hai chiesto .. ma, la tua risposta: args rappresenta spesso gli argomenti senza nome della funzione, e kwargs sarebbero gli argomenti con nome (ad esempio my_func (arg1, arg2, namearg1 =' dd ', namearg2 = 'ddd') ..... poi in my_func (self, * args, ** kwargs), args = [arg1, arg2], kwargs = {'namearg1': 'dd', 'namearg2': 'ddd'} ... è utile passare questi alla sovrascrittura della funzione in modo da non interrompere alcuna funzionalità nella funzione originale –

4

Questo è quello che uso nei miei modelli per salvare una nuova miniatura se l'immagine caricata è cambiata. È basato su un altro DjangoSnippet ma non ricordo chi ha scritto l'originale - se sai per favore aggiungi un commento in modo che io possa accreditarli.

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

C'è la funzione 'image.thumbnail (size)' che fa questo per te, in realtà non lo fai bisogno di calcolare larghezza/altezza/rapporto ecc. –

2

Override del metodo Save è una buona opzione, ma sarei più tentati di usare un signal in questo caso. I segnali Django ti permettono di "ascoltare" i vari eventi di un determinato tipo di modello; in questo caso, ti interesserebbe l'evento post_save.

Di solito mi iscrivo a tali segnali nel mio file models.py. Codice per voi sarebbe simile a questa:

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

Questo è un po 'di confusione no? Forse rinominare la chiamata della funzione resize_image? – justin

2

Ryan è segnali corretti sono un modo migliore per andare però il vantaggio del sovresposta SAVE è che possiamo ottenere i percorsi vecchi e nuovi di immagini, vedere se l'immagine ha modificato (e se ha creato una nuova miniatura), salvare l'istanza del modello e quindi eliminare la vecchia immagine e la miniatura.

Ricorda che django non pulisce le vecchie immagini per te, a meno che tu non abbia uno script per controllare che le immagini/le anteprime siano ancora in uso e ripulire tutto ciò che non lo è, perderai spazio su disco. (Questo potrebbe o non potrebbe essere un problema per te a seconda delle dimensioni dell'immagine e della frequenza degli aggiornamenti)

Non sono sicuro di come potresti farlo con un segnale post_save, e non ne so abbastanza dei segnali (Questa è la ricerca per stasera!) Per sapere se c'è un segnale pre-scatto adeguato. Se trovo uno allora scriverò di nuovo il codice sopra per usare i segnali come un listener generico di pre-salvataggio.

+1

Sì, c'è anche un segnale pre_save. Trovo che i segnali siano generalmente più piacevoli con cui lavorare, ma lì sono sicuramente i tempi in cui è preferibile il salvataggio di override(). –

0

Lo giuro anche di Justin Driscoll django-photologue è ottimo anche per il ridimensionamento. E:

  1. ridimensiona (cioè può essere scalata a un'altezza o larghezza proporzionalmente)
  2. Colture (tipo-di intelligente: dal centro, superiore, sinistro, inferiore oppure destra)
  3. Opzionalmente upsizes
  4. può aggiungere effetti (come "colore", "luminosità", "contrasto" e "nitidezza", così come filtri come "Trova bordi" e "rilievo". Affila le tue immagini. Fare il tuo miniature in bianco e nero.)
  5. può aggiungere semplice filigrana
  6. Cache i risultati

Fondamentalmente è impressionante.

2

si può dare una prova per:

image headcut

caratteristiche:

  • Consente di impostare il campo visivo di un'immagine ... teste non otterranno più tagliare. immagini cross-site collegando
  • di installazione e l'uso di semplice
  • video miniature
  • Impedisce
1

Un'altra alternativa è quella di utilizzare Imagemagick direttamente, se si vuole evitare alcune difficoltà con cuscino e Python 3, come this one .

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

Si potrebbe chiamare questo il modello di salvare o su un tag modello per generare una copia una tantum di manipolare il file originale in un modo di file-caching, o anche un compito di sedano.

È possibile ottenere un esempio di come ho usato questo in uno dei miei progetti:

Problemi correlati