1. 程式人生 > >django上傳圖片,ImageField

django上傳圖片,ImageField

今天看django的時候,突然發現model裡有個ImageField,原以為django的model只是一個ORM的框架,沒想到連上傳圖片到伺服器都可以搞定,於是結合例子嘗試了一下。ImageField需要PIL的支援,所以沒裝PIL這個庫的需要先安裝。

首先建立兩個model:

class Item(models.Model):
    name = models.CharField(max_length=250)
    description = models.TextField()
    
    class Meta:
        ordering = ['name']
        
    def __unicode__(self):
        return self.name
    
    @models.permalink
    def get_absolute_url(self):
        return ('item_detail', None, {'object_id':self.id})


class Photo(models.Model):
    item = models.ForeignKey(Item)
    title = models.CharField(max_length=100)
    image = models.ImageField(upload_to='photos')
    caption = models.CharField(max_length=250, blank=True)
    
    class Meta:
        ordering = ["title"]
        
    def __unicode__(self):
        return self.title
    
    @models.permalink
    def get_absolute_url(self):
        return ('photo_detail', None, {'object_id':self.id})
    

class PhotoInline(admin.StackedInline):
    model = Photo
    
class ItemAdmin(admin.ModelAdmin):
    inlines = [PhotoInline]
        

然後在settings.py中加入MEDIA_ROOT:
MEDIA_ROOT = '/var/www/gallery/media/'
由於我是用apache跑的,所以需要注意目錄許可權。

這裡

upload_to='photos' 這句表示上傳的檔案會存放在$MEDIA_ROOT/photos/ 下面,也就是放在 /var/www/gallery/media/photos/ 下。

之後一番syncdb,開啟admin頁面,看到剛才建立的model,新增一條資料,上傳圖片,成功了,django真是強大!

然後再修改剛才新增的那條記錄的修改介面,可以看到剛才上傳的圖片的超接:


點開圖片的超連結,卻顯示404 not found ,這可奇怪了,在伺服器上明明已經有這個檔案,並且資料庫都新增正確了,怎麼讀不回來呢。

這裡發現有個奇怪的地方,開啟的圖片url為:http://10.40.3.164:8090/admin/gallery/item/1/photos/github-logo.png/

超連結裡面的href值為:photos/github-logo.png,這個有問題啊,圖片應該是相對獨立的,且不說這裡load不到圖片,如果真實使用的時候,用這個url肯定不對。

好吧,看下ImageField 的原始碼吧。

django/db/models/fields/files.py 中有:

class ImageField(FileField):
ImageField本身沒有什麼和 url有關的東西,繼續看它的父類: FileField
class FileField(Field):

    # The class to wrap instance attributes in. Accessing the file object off
    # the instance will always return an instance of attr_class.
    attr_class = FieldFile

FileField 中有個屬性 attr_class 這裡註釋說裡面屬性都是從這裡配置的那個類那來的。那我們繼續看 FieldFile 類:

class FieldFile(File):
    def _get_url(self):
        self._require_file()
        return self.storage.url(self.name)
    url = property(_get_url)

果然有個叫做 url 的屬性,但是這個屬性有是通過 storage.url方法返回的。由於是存的檔案,那很可能是FileStorage,先look一下吧。

django/core/files/storage.py  中有個FileSystemStorage類,其中有個url方法:

    def url(self, name):
        if self.base_url is None:
            raise ValueError("This file is not accessible via a URL.")
        return urljoin(self.base_url, filepath_to_uri(name))

這裡的已經比較清楚了,url是由 self.base_url 加上一段檔案的filename組成的,這裡的 self.base_url 的值是由 MEDIA_URL 這個配置選項決定的,再看下 django/conf/global_setting.py 檔案,裡面的MEDIA_URL預設為空字串。

在我們專案的settings.py中加入 MEDIA_URL 這個配置選項:

MEDIA_URL='/media/'

重啟apache後,再次開啟那條記錄的修改頁面,這時候通過firebug看到超連結的href屬性已經變為 /media/photos/github-logo.png

點選超連結,請求的是 http://10.40.3.164:8090/media/photos/github-logo.png 頁面,好吧這就是href屬性是否以 “/" 開頭的不同。

但是此時還是顯示錯誤,但是問題已經很明顯了,和設定靜態檔案一樣,需要在專案的urls.py中加點東西:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
OK,再試一下,圖片果然出來了。

至此,ImageField 已經可以使用了 :-)