Django admin file upload with current model id

前端 未结 5 1040
轮回少年
轮回少年 2020-12-01 03:29

I\'m trying to create a simple photo gallery with the default Django admin. I\'d like to save a sample photo for each gallery, but I don\'t want to keep the filname. Instead

5条回答
  •  温柔的废话
    2020-12-01 03:54

    In django 1.7, the proposed solutions didn't seem to work for me, so I wrote my FileField subclasses along with a storage subclass which removes the old files.

    Storage:

    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
    

    and my upload_to callables look like this:

    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))
    

提交回复
热议问题