Возможно, плохая реализация сигналов Django

Недавно я развернул проект полного стека с использованием Django, Gunicorn и Nginx. Вкратце, это сайт для моего друга, пространство, в котором он может делиться и публиковать свои последние действия (например, опубликованные альбомы, фотосессии и статьи). Я попытался автоматизировать сбор информации для заполнения экземпляра модели при загрузке файла (для более быстрой, чистой и простой загрузки из панели администратора), используя несколько сигналов Django. Моя идея заключалась в следующем: если я загружу из админ-панели идеально отформатированный файл (.docx), то с помощью сигналов и других функций я смогу заполнить его информацией и все. Но, если я удалю экземпляр (т.е. альбом или статью), я бы хотел, чтобы это повлияло на файлы (я имею в виду удаление файлов из папки media на сервере). Прежде чем выложить свой код, хочу сказать, что в разработке этот подход работает отлично и для разных моделей он одинаков. К сожалению, в продакшене это не так. Точнее, загрузка файлов работает отлично, обработка иногда нет (и я остаюсь с пустыми полями), удаление файлов для одних моделей работает, для других нет вообще.

settings.py

INSTALLED_APPS = [
    'musicsite.apps.MusicsiteConfig',
    ...
]

apps.py

from django.apps import AppConfig

class MusicsiteConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'musicsite'

    def ready(self):
        from musicsite import signals

models.py

class Bio(models.Model): 
    class Meta:
        verbose_name_plural = "Bio"
        
    italian_bio = models.TextField(*nothing important*)
    english_bio = models.TextField(*nothing important*)
    last_updated = models.DateField(auto_now=True)
    doc_file = models.FileField(upload_to="documents/", *nothing important*)

signals.py

@receiver(pre_save, sender=Bio)
def replace_file(sender, instance, **kwargs):
    if not instance.pk:
        return
    try: 
        old_instance = Bio.objects.get(pk=instance.pk)
    except Bio.DoesNotExist:
        return
    old_doc_name = os.path.basename(old_instance.doc_file.name) if (old_doc := old_instance.doc_file) else None
    new_doc_name = os.path.basename(instance.doc_file.name) if (new_doc := instance.doc_file) else None
    
    if old_doc_name and (not new_doc_name or old_doc_name != new_doc_name):
        old_doc_path = Path(settings.MEDIA_ROOT) / old_doc.name
        if old_doc_path.is_file():
            safe_unlink(old_doc_path, retries=100)

@receiver(pre_delete, sender=Bio)
def delete_file(sender, instance, using, **kwargs):
    if instance.doc_file:
        doc_file_path = Path(settings.MEDIA_ROOT) / instance.doc_file.name
        if os.path.isfile(doc_file_path):
            safe_unlink(doc_file_path, retries=100)

@receiver(post_save, sender=Bio)
def process_biography_file(sender, instance, **kwargs):
    if hasattr(instance, '_disable_signals') and instance._disable_signals:
        return
    temp_it, temp_en = "", ""
    if instance.doc_file:
        full_path = Path(settings.MEDIA_ROOT) / instance.doc_file.name
        try:
            temp_data = handle_file(full_path)
            temp_it, temp_en = generate_biography(temp_data)
        except Exception as e:
            instance.delete()
            raise ValidationError(f"Error! {e}")
        with transaction.atomic():
            if instance.italian_bio == "":
                if temp_it != "":
                    instance.italian_bio = temp_it
                else:
                    instance.delete()
                    raise ValidationError("The italian field should not be empty!")
            if instance.english_bio == "":
                if temp_it != "":
                    instance.english_bio = temp_en
                else:
                    instance.delete()
                    raise ValidationError("The english field should not be empty!")
            # PREVENTS INFINITE RECURSION
            instance._disable_signals = True
            instance.save(update_fields=['italian_bio', 'english_bio',])
            instance._disable_signals = False
    else:
        if instance.italian_bio == "" or instance.english_bio == "":
            instance.delete()
            raise ValidationError("Fields cannot be empty! Upload a file or provide values for the admin fields!")
Вернуться на верх