Перезапись изображения с тем же именем — Django

В моем проекте пользователи загружают изображение профиля. Я сохраняю изображение профиля как userID.jpg. Если они загружают новое изображение профиля, я хочу перезаписать старое изображение профиля, чтобы не тратить место на диске. Просматривая ранее заданные вопросы в stackoverflow, я переопределил OverwriteStorage:

class OverwriteStorage(FileSystemStorage):
    def get_available_name(self, name, max_length=None):
        if self.exists(name):
             os.remove(os.path.join(settings.MEDIA_ROOT, name))
        return name

Когда я загружаю изображение профиля, я вижу в каталоге на своем компьютере, что изображение было успешно перезаписано. Изображение сохраняется с путем «media/profile/userID.jpg». Однако, когда я показываю изображение на своем сайте, это все еще старое изображение. Через сайт Django при открытии пути вижу старую картинку, а при попытке изменить через админку выдает следующую ошибку:

[WinError 32] The process cannot access the file because it is being used by another process: '\media\\profile\\userID.jpg'

Я предполагаю, что неправильно перезаписываю файл, а другой экземпляр все еще открыт, и чтобы решить эту проблему, мне нужно правильно закрыть изображение перед перезаписью. Я пытался это сделать, но безуспешно.

Вероятно, кеш вашего браузера. Вы пытались перезагрузить страницу с помощью ctrl + shift + r?

Debendra 10.04.2019 05:10
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
1 497
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Попросите его переименовать старый в userID-old.jpg, а затем сохраните userID.jpg. Это произойдет так быстро, что никто, скорее всего, не заметит, что это происходит.

Ответ принят как подходящий

Я сделал что-то подобное, но использовал сигналы для обновления и удаления изображений.

Во-первых, я определил имя изображения в helpers.py

from django.conf import settings
from datetime import datetime

def upload_to_image_post(self, filename):
    """
    Stores the image in a specific path regards to date 
    and changes the name of the image with for the name of the post
    """    
    ext = filename.split('.')[-1]
    current_date = datetime.now()

    return '%s/posts/main/{year}/{month}/{day}/%s'.format(
        year=current_date.strftime('%Y'), month=current_date.strftime('%m'), 
        day=current_date.strftime('%d')) % (settings.MEDIA_ROOT, filename)

Итак, я назвал определение в своей модели, особенно в поле изображения

from django.db import models
from django.utils.text import slugify
from .helpers import upload_to_image_post

class Post(models.Model):
    """
    Store a simple Post entry. 
    """
    title = models.CharField('Title', max_length=200, help_text='Title of the post')
    body = models.TextField('Body', help_text='Enter the description of the post')   
    slug = models.SlugField('Slug', max_length=200, db_index=True, unique=True, help_text='Title in format of URL')        
    image_post = models.ImageField('Image', max_length=80, blank=True, upload_to=upload_to_image_post, help_text='Main image of the post')

    class Meta:
        verbose_name = 'Post'
        verbose_name_plural = 'Posts'

Наконец, я определил сигналы для обновления или удаления изображения до того, как в модели произойдут действия (обновление или удаление).

import os
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import pre_delete, pre_save
from .models import Post

@receiver(pre_delete, sender=Post)
def post_delete(sender, instance, **kwargs):
    """
    Deleting the specific image of a Post after delete it
    """
    if instance.image_post:
        if os.path.isfile(instance.image_post.path):
            os.remove(instance.image_post.path)

@receiver(pre_save, sender=Post)
def post_update(sender, instance, **kwargs):
    """
    Replacing the specific image of a Post after update
    """
    if not instance.pk:
        return False

    if sender.objects.get(pk=instance.pk).image_post:
        old_image = sender.objects.get(pk=instance.pk).image_post
        new_image = instance.image_post
        if not old_image == new_image:
            if os.path.isfile(old_image.path):
                os.remove(old_image.path)
    else:
        return False

Надеюсь, это помогло вам.

Спасибо, теперь все отлично работает; однако раньше я никогда не использовал сигналы, и теперь мой сайт стал немного медленнее. Это нормально? Сигналы лучше класть в models.py или в отдельный файл?

Ibrahim Fakih 10.04.2019 16:37

Вы говорите, что ваш сайт работает медленно из-за сигналов? или ваш сайт был медленным до этого? Я использую сигналы, и производительность хорошая.

Samir Hinojosa 10.04.2019 19:59

Это интересно получать и обрабатывать сигналы. В некоторых случаях это может быть удобнее, чем OverwriteStorage(FileSystemStorage).

Но os.remove(filename) небезопасно/не работает без локальной файловой системы. Я рекомендую использовать API хранилища файлов Django.

from django.core.files.storage import default_storage

os.path.isfile(path)  # worse
default_storage.exists(path)  # better

os.remove(path)  # worse
default_storage.delete(path)  # better

Другие вопросы по теме