2009-07-22 145 views
11

我是Django(和Python)的新手,在嘗試使用其他人的應用程序之前,我一直在嘗試自己解決一些問題。我無法理解Django(或Python)做事的方式。我試圖解決的是如何調整圖像的大小,一旦它被上傳。我有我的模型很好地建立並插入到管理,並且將圖像上傳罰款的目錄:用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 

現在我在與正在該文件,使一個新的文件爲縮略圖,麻煩的事情。就像我說的,我想知道如何去做,而不使用別人的應用程序(現在)。我已得到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' 

...但這最終弄得我......因爲我不知道這是如何「在符合」我的Django應用程序?真的,這是僅僅製作已成功上傳圖像的縮略圖的最佳解決方案嗎?任何人都可能向我展示一種像我這樣的初學者可以學會如何正確做到這一點的良好,堅實和體面的方式?就像在知道在哪裏放置這種代碼(models.py?forms.py?...)以及它如何在上下文中工作一樣? ...我只需要一點幫助理解和解決這個問題。

謝謝!

回答

7

如果這對你OK,有一個Django應用程序準備好了,正好做你想要什麼: https://github.com/sorl/sorl-thumbnail

+2

謝謝葉節點,我我聽說過這個,我會看看它。但我希望能夠嘗試和理解首先做事情的基本方式,希望能夠讓我更好地理解它是如何工作的......也許這不值得這樣做? ...但我認爲這樣做是值得的,以使我成爲更好的Python/Django開發人員? – littlejim84 2009-07-22 12:47:09

+0

如果您想了解詳細信息,請查看其他答案,但請記住它是針對用戶端表單的。 – leafnode 2009-07-22 12:48:41

+1

它是deaaaaaaaaaaaaad – titus 2011-12-03 06:19:10

2

不知道你發送的代碼,因爲我從來沒有使用模型本身,但還有另一種方法。

您可以實現自己的FileUploadHandler來處理圖像文件上傳。例子是 here。 緊接着第37行(dest.close())使用thumbnail(upload_dir + upload.name)函數(您發送的函數)。

希望它可以幫助你。

2

關鍵問題是:應該生成縮略圖嗎?

  1. 動態當用戶請求縮略圖時?
  2. 或者,您是否希望在數據庫中插入/更新某個國家/地區時創建物理磁盤文件。

如果(1)我建議你創建一個映射到url /flagthumbnail/countryid的視圖。那麼該視圖的方法必須:

  1. 從數據庫
  2. 獲得國家例如讀取標籤用圖像爲PIL圖像和調整其大小。
  3. 使用正確的內容類型創建(並返回)HTTPResponse並將PIL圖像寫入響應。

無論何時您需要顯示縮略圖標誌,只需使用<a href="/flagthumbnail/countryid">即可。

如果(2),您可以連接到Django的django.db.models.signals.post_save信號並在信號處理程序中創建並保存縮略圖文件。

2

我想這取決於你如何以及何時使用你的縮略圖。

如果你想創造一些縮略圖,每一個國家保存時間,你可以做到這一點,像這樣:

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) 

如果你不是100%確定什麼尺寸,你需要你的形象,你可以在最後一刻調整它們的大小我已經看到這有效地完成了一個模板標籤(我相信在Pinax上的一個版本)。您可以創建一個模板標籤,用於獲取圖像和大小,然後根據需要創建並保存適當大小的圖像,或者在出現之前顯示先前創建的圖像。它工作得很好。

4

這是我在模型中用來保存新的縮略圖(如果上傳的圖像已更改)。它基於另一個DjangoSnippet,但是我不記得誰是原創者 - 如果您知道請添加評論,以便我可以信任他們。

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 
2

重寫保存方法是一個不錯的選擇,但我更傾向於在這種情況下,使用signal。 Django信號允許您「聽」給定模型類型的各種事件;在這種情況下,您會對post_save事件感興趣。

我通常在我的models.py文件中訂閱這樣的信號。代碼,你會是這個樣子:

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) 
2

瑞安是正確的信號是一個更好的辦法但是去被覆蓋的優勢節省的是,我們可以得到新老圖片的路徑,看到如果圖像更改(如果它創建了新的縮略圖),保存模型實例,然後刪除舊的圖像和縮略圖。

請記住,django並沒有爲您清理舊圖像,除非您有腳本檢查圖像/縮略圖是否仍在使用中,並且清除了任何不是您的內容,否則會泄漏磁盤空間。 (根據圖像大小和更新頻率,這可能會也可能不會成爲問題)

我不確定您如何使用post_save信號做到這一點,但我對信號不夠了解(這是今晚的研究!)知道是否有合適的pre_save信號。如果我找到一個,那麼我將重新編寫上面的代碼,將信號用作通用預保存列表器。

0

我也發誓賈斯汀德里斯科爾的django-photologue也是偉大的調整。它:

  1. 大小調整(即可以擴展到一個高度或成比例的寬度)
  2. 作物(種類-的智能:從中心,頂部,左邊,底部或右)
  3. 可選upsizes
  4. 可以添加效果(如「顏色」,「亮度」,「對比度」和「銳度」,以及像「查找邊緣」和「浮雕」。銳化圖像過濾器。讓你的縮略圖黑色和白色。)
  5. 可以添加簡單水印
  6. 緩存結果

基本上它的真棒。

2

你可以給一個嘗試:

image headcut

特點:

  • 允許您設置的圖像關注的中心......頭將不會再切割。
  • 視頻縮略
  • 防止跨站點圖像鏈接
  • 簡單的設置和使用
1

另一種方法是直接使用ImageMagick的,如果你想避免與枕頭和Python 3,如this one一些困難。

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

你可以稱之爲型號保存,或在模板標籤生成的文件緩存的方式原始文件的一次性拷貝操作,甚至是芹菜任務。

你可以得到我如何在我的一個項目中使用這樣的一個例子: