我正在尝试在django中生成动态文件路径。我想建立一个这样的文件系统:
-- user_12
--- photo_1
--- photo_2
--- user_ 13
---- photo_1
我找到了一个相关问题:Django Custom image upload field with dynamic path
在这里,他们说我们可以更改upload_to路径并转到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)
但是,这仍然不是动态的,我想将Car id赋予图像名称,并且在Car定义完成之前我无法分配id。那么如何创建一个带有车牌ID的路径?
答案 0 :(得分:64)
您可以在upload_to
参数中使用callable,而不是使用自定义存储。请参阅docs,并注意在调用函数时可能尚未设置主键的警告(因为可以在将对象保存到数据库之前处理上载),因此使用ID
可能无法实现。您可能会考虑在模型上使用另一个字段,例如slug。 E.g:
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)
答案 1 :(得分:5)
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
ID。
在大多数情况下,此对象不会 已经保存到数据库了, 所以,如果它使用默认的AutoField, 它可能还没有它的价值 主键字段。
答案 2 :(得分:4)
你可以使用如下的lambda函数,请注意,如果instance是new,那么它将没有实例id,所以使用其他东西:
logo = models.ImageField(upload_to=lambda instance, filename: 'directory/images/{0}/{1}'.format(instance.owner.id, filename))
答案 3 :(得分:3)
我的解决方案不优雅,但有效:
在模型中,使用需要id / pk
的标准函数def directory_path(instance, filename):
return 'files/instance_id_{0}/{1}'.format(instance.pk, filename)
views.py中的保存表格如下:
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值。空=真
答案 4 :(得分:2)
派对很晚,但这个适合我。
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)
答案 5 :(得分:1)
DjangoSnippets有两种解决方案
答案 6 :(得分:1)
您可以覆盖模型的save
方法:
def save_image(instance, filename):
instance_id = f'{instance.id:03d}' # 001
return f'{instance_id}-{filename.lower()}' # 001-foo.jpg
class Resource(models.Model):
photo = models.ImageField(upload_to=save_image)
def save(self, *args, **kwargs):
if self.id is None:
photo = self.photo
self.photo = None
super().save(*args, **kwargs)
self.photo = photo
if 'force_insert' in kwargs:
kwargs.pop('force_insert')
super().save(*args, **kwargs)
答案 7 :(得分:0)
This guy有办法做动态路径。我们的想法是设置您喜欢的存储并使用函数自定义“upload_to()”参数。
希望这有帮助。
答案 8 :(得分:0)
我找到了一个不同的解决方案,这个解决方案很脏,但它确实有效。您应该创建一个新的虚拟模型,它与原始模型自我同步。我并不为此感到自豪,但没有找到另一种解决方案。在我的情况下,我想上传文件,并将每个文件存储在以模型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中都很新,但对我来说似乎没问题。
另一种解决方案:
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)
答案 9 :(得分:0)
如果模型实例尚未保存到数据库中,则主键(id)可能不可用,我编写了我的FileField子类,它在模型保存上移动文件,以及一个删除旧文件的存储子类。 / p>
存储
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 callable看起来像这样:
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))
答案 10 :(得分:0)
MEDIA_ROOT/
/company_Company1/company.png
/shop_Shop1/shop.png
/bikes/bike.png
def photo_path_company(instance, filename):
# file will be uploaded to MEDIA_ROOT/company_<name>/
return 'company_{0}/{1}'.format(instance.name, filename)
class Company(models.Model):
name = models.CharField()
photo = models.ImageField(max_length=255, upload_to=photo_path_company)
def photo_path_shop(instance, filename):
# file will be uploaded to MEDIA_ROOT/company_<name>/shop_<name>/
parent_path = instance.company._meta.get_field('photo').upload_to(instance.company, '')
return parent_path + 'shop_{0}/{1}'.format(instance.name, filename)
class Shop(models.Model):
name = models.CharField()
photo = models.ImageField(max_length=255, upload_to=photo_path_shop)
def photo_path_bike(instance, filename):
# file will be uploaded to MEDIA_ROOT/company_<name>/shop_<name>/bikes/
parent_path = instance.shop._meta.get_field('photo').upload_to(instance.shop, '')
return parent_path + 'bikes/{0}'.format(filename)
class Bike(models.Model):
name = models.CharField()
photo = models.ImageField(max_length=255, upload_to=photo_path_bike)
答案 11 :(得分:0)
另一种解决方案:
useEffect(() => {
return () => {
setStartDate(null);
}
}, []);
答案 12 :(得分:0)
方法将
def user_directory_path(field_name):
1
在模型中,您可以根据需要拥有任意多个ImageField。例子
照片=模型.ImageField(upload_to = user_directory_path('照片'),null =真,空白=真,)
passport_photo = models.ImageField(upload_to = user_directory_path('passport_photo'),null = True,blank = True,)