我确实有 Aidan Ewen 解决方案的更可定制的实现。
什么是新的?
- 您可以将要在文件名中使用的字段作为列表发送(如预购)
- ^ 其中一个必须是唯一的
- ^ 否则,此列表必须包括(至少一个)唯一一起字段的元组
- ^ 否则,您发送的字段将被忽略,并将使用 uuid4 作为文件名
示例 1:
image = models.ImageField(upload_to=PathAndRename('images/').wrapper)
filename = {pk}.{ext}
# default is pk for filenames
示例 2:
name = models.CharField(max_length=20) # not unique
image = models.ImageField(upload_to=PathAndRename('images/', ['name']).wrapper)
filename = {uuid4}.{ext}
# if given fields are did not accepted will use the uuid4
示例 3:
name = models.CharField(max_length=20, unique=True)
no = models.CharField(max_length=10)
image = models.ImageField(upload_to=PathAndRename('images/', ['name','no']).wrapper)
filename = {name}_{no}.{ext}
# one unique field is enough to use all of the given fields in the filename
示例 4:
name = models.CharField(max_length=20) # not unique
no = models.CharField(max_length=10) # not unique
image = models.ImageField(upload_to=PathAndRename('images/', ['name','no']).wrapper)
class Meta:
unique_together = ('name', 'no')
# (('name', 'no'),) is acceptable too or multiple unique togethers
filename = {name}_{no}.{ext}
# if one of the unique together fields exists in the given fields, will use all of the given fields in the filename
我可能忘了再举一些例子,但你可以从下面的代码中理解:
class PathAndRename:
"""
fields to use for naming, order is important
"""
def __init__(self, path, fields_to_use=('pk',)):
self.path = path
self.fields_to_use = fields_to_use
def wrapper(self, instance, filename):
# multiple extensions
ext = '.'.join(filename.split('.')[1:])
# check the uniqueness of the fields given for filename
if self.is_any_unique_exist(instance):
# if any unique field exist in the given list
# create filename by using given field values
filename = '{}.{}'.format(self.get_filename_by_fields(instance), ext)
# else check the existence of at least one unique together
elif self.is_any_unique_together_exist(instance):
# create filename by using given field values
filename = '{}.{}'.format(self.get_filename_by_fields(instance), ext)
# if any unique or unique together not exists
else:
# then create a filename by using uuid4
filename = '{}.{}'.format(uuid4().hex, ext)
# return the whole path to the file
return os.path.join(self.path, filename)
def is_any_unique_exist(self, instance):
if 'pk' in self.fields_to_use:
return True
return any([instance._meta.get_field(field).unique for field in self.fields_to_use if hasattr(instance, field)])
def is_any_unique_together_exist(self, instance):
if hasattr(instance._meta, 'unique_together'):
if isinstance(instance._meta.unique_together, (list, tuple)):
for uniques in instance._meta.unique_together:
# if any one of the unique together set is exists in the fields to use
if all(map(lambda field: field in self.fields_to_use, uniques)):
return True
else:
if all(map(lambda field: field in self.fields_to_use, instance._meta.unique_together)):
return True
return False
def get_filename_by_fields(self, instance):
return '_'.join([str(getattr(instance, field)) for field in self.fields_to_use])
警告:当您放弃使用这些解决方案时,针对此 upload_to 问题的每个基于方法的解决方案都会对执行的迁移文件造成问题。如果您使用这些解决方案一段时间然后删除它们,旧的迁移将由于这些方法不存在而失败。(当然,你可以通过修改旧的迁移文件来解决这个问题)