今天看django的时候,突然发现model里有个ImageField,原以为django的model只是一个ORM的框架,没想到连上传图片到服务器都可以搞定,于是结合例子尝试了一下。ImageField需要PIL的支持,所以没装PIL这个库的需要先安装。

首先建立两个model:

[python] view plaincopy

  1. class Item(models.Model):  
  2. name = models.CharField(max_length=250)  
  3. description = models.TextField()  

  4. class Meta:  

  5. ordering = ['name']  

  6. def unicode(self):  

  7. return self.name  

  8. @models.permalink  

  9. def get_absolute_url(self):  
  10. return ('item_detail', None, {'object_id':self.id})  

  11. class Photo(models.Model):  

  12. item = models.ForeignKey(Item)  
  13. title = models.CharField(max_length=100)  
  14. image = models.ImageField(upload_to='photos')  
  15. caption = models.CharField(max_length=250, blank=True)  

  16. class Meta:  

  17. ordering = ["title"]  

  18. def unicode(self):  

  19. return self.title  

  20. @models.permalink  

  21. def get_absolute_url(self):  
  22. return ('photo_detail', None, {'object_id':self.id})  

  23. class PhotoInline(admin.StackedInline):  

  24. model = Photo  

  25. class ItemAdmin(admin.ModelAdmin):  

  26. inlines = [PhotoInline]  

然后在settings.py中加入MEDIA_ROOT:

[python] view plaincopy

  1. MEDIA_ROOT = '/var/www/gallery/media/'  

由于我是用apache跑的,所以需要注意目录权限。

这里

upload_to='photos' 这句表示上传的文件会存放在$MEDIA_ROOT/photos/ 下面,也就是放在 /var/www/gallery/media/photos/ 下。

之后一番syncdb,打开admin页面,看到刚才建立的model,添加一条数据,上传图片,成功了,django真是强大!

然后再修改刚才添加的那条记录的修改界面,可以看到刚才上传的图片的超接:

image

点开图片的超链接,却显示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 中有:

[python] view plaincopy

  1. class ImageField(FileField):  

ImageField本身没有什么和 url有关的东西,继续看它的父类: FileField

[python] view plaincopy

  1. class FileField(Field):  

  2. The class to wrap instance attributes in. Accessing the file object off

  3. the instance will always return an instance of attr_class.

  4. attr_class = FieldFile  

FileField 中有个属性 attr_class 这里注释说里面属性都是从这里配置的那个类那来的。那我们继续看 FieldFile 类:

[python] view plaincopy

  1. class FieldFile(File):  
  2. def _get_url(self):  
  3. self._require_file()  
  4. return self.storage.url(self.name)  
  5. url = property(_get_url)  

果然有个叫做 url 的属性,但是这个属性有是通过 storage.url方法返回的。由于是存的文件,那很可能是FileStorage,先look一下吧。

在django/core/files/storage.py  中有个FileSystemStorage类,其中有个url方法:

[python] view plaincopy

  1. def url(self, name):  
  2. if self.base_url is None:  
  3. raise ValueError("This file is not accessible via a URL.")  
  4. 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 这个配置选项:

[python] view plaincopy

  1. 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中加点东西:

[python] view plaincopy

  1. urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)  

OK,再试一下,图片果然出来了。

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