Trong django 1.7, các giải pháp được đề xuất dường như không hoạt động đối với tôi, vì vậy tôi đã viết các lớp con FileField của mình cùng với một lớp con lưu trữ loại bỏ các tệp cũ.
Bảo quản:
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
và callables upload_to của tôi trông như thế này:
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))
Tại sao điều này sẽ xứng đáng với một downvote? Nó chắc chắn là một câu hỏi có chất lượng tốt hơn so với một số. – hop
Không có cách nào đáng tin cậy để biết id của hồ sơ tiếp theo trước thời hạn. Bạn có thể nhận được id sau khi bản ghi được tạo ban đầu, nhưng điều này cũng phải tuân theo điều kiện chủng tộc. Lời khuyên của tôi - chọn một thứ khác ngoài id để đặt tên tệp của bạn. – Brandon
Ví dụ: dấu thời gian hiện tại + micro giây – ilvar