原文地址:http://plq168.blog.163.com/blog/static/53101462201093115050188/
参考于http://adil.2scomplement.com/2009/01/django-playing-with-upload_to-field/
django提供了两种字段类型models.FileField与models.ImageField,用于保存上传文件与图
象.这两类字段提供了一个参数'upload_to',用于定义上传文件保存的路径(完整的路径为settings.MEDIA_ROOT + upload_to).
运行时创建基于时间的文件夹
你可以使用参数upload_to,上传文件到一个基于文件时间的文件夹中.
假如你要创建一个相册,要把图片传到名称包含年与月的文件夹中.
class Photo(models.Model):
original = models.ImageField(upload_to='album/%m-%Y/')
每次保存一个图片时,django会在MEDIA_ROOT/album/中创建一个'MM-YYYY'文件夹,用来保存图片.
使用django源码来说明实现方式:
在django/db/models/fields/fiels.py中
class FileField(Field):
....
def __init__(self,...,upload_to='',...):
...
self.upload_to = upload_to
if callable(upload_to):
self.generate_filename = upload_to
...
说明:
如果upload_to是可执行的,则FileField的方法generate_filename的默认方法就会被覆盖
保存时的操作:
Class FileField(Field):
...
def pre_save(self, model_instance, add):
file = super(FileField, self).pre_save(model_instance, add) # file的类型是FieldFile
if file and not file._committed:
file.save(file.name, file, save=False)
return file
而file中save的代码如下:
class FieldFile(File):
...
def save(...):
name = self.field.generate_filename(self.instance, name)
self.name = self.storage.save(name, content)
...
从以上代码看来,如果upload_to是可执行的,FieldFile的方法save会调用generate_filename方
法,但实际上是已替换为upload_to.如upload_to不能执行,就会执行FieldFile的方法save会调用
FileField的默认方法generate_filename方法.
class FieldFile(Field):
...
def generate_filename(self, instance, filename):
return os.path.join(self.get_directory_name(), self.get_filename(filename))
def get_directory_name(self):
return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
def get_filename(self, filename):
return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename)))
上面的实例中,为了产生上传文件夹名,实际上是执行了get_directory_name,而这个函数中包含
了upload_to,把实际的数据代入后,文件名是由下面的代码计算出来的:
datetime.datetime.now().strftime('album/%m-%Y/'),'%m-%Y'是时间格式符.
动态更改上传文件夹
以下面的model为例:
class MediaContent(models.Model):
content_file = models.FileField()
content_type = models.CharField(max_length=1,
choices=(('I','Image'),('V','Video'),('A','Audio'),('D','Doc')))
#other fields
MediaContent文件提供了各种媒体文件,如图象,视频,音频,文档与pdf文件.我要对这些媒体文件
提供一致的操作,但不同的文件类型上传到不同的文件夹中.所以在保存MediaContent实例前需要
更改upload_to.
使用一个callback作为upload_to
django可以让你提供一个callback作为upload_to值.这个回调函数有如下参数:
1) self MediaConten实例
2) instance 真正的FileField或ImageField实例(此处实际上是content_file)
3) filename 当保存文件时需要传递的文件名
使用这个技术,按下面代码来实现:
class MediaContent(models.Model):
content_file = models.FieldFile(upload_to=get_media_upload_to)
content_type = models.CharField(max_length=1,
choices=(('I','Image'),('V','Video'),('A','Audio'),('D','Doc')))
#other fields
def get_media_upload_to(instance, filename):
"""
A simple way to return the full path
"""
paths = { 'I':'images/', 'V':'videos/', 'A':'audio/', 'D':'documents'/ }
return settings.MEDIA_ROOT + 'content/' + paths[instance.content_type] + filename
每次调用media_content_object.content_file.save(filename, mem_file)
时,get_media_upload_to就会被调用,你可以基于content_type来确定文件路径.
运行时修改upload_to
你可以设置upload_to为一个固定的值,然后在保存对象时更改为你要的位置
def save_content(request):
#if image
MediContent._meta.get_field('content_file').upload_to='content/images/'
#if video
MediContent._meta.get_field('content_file').upload_to='content/videos/'
#if audio
MediContent._meta.get_field('content_file').upload_to='content/audios/'
#if doc
MediContent._meta.get_field('content_file').upload_to='content/documents/'
#if pdf
MediContent._meta.get_field('content_file').upload_to='content/acrobat/'
#other types