Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django pre_delete signal gets ignored

Here is what is run on pre_delete of model Document. This code gets royally ignored when put in a separate file (signals.py) as suggested best practice. When put in model file, it works fine.

from django.db.models.signals import pre_delete, pre_save
from django.dispatch import receiver
from _myTools.functions import ImageProcessing
from content_image.models import Document
import os

# Image deletion when deleting entire entry
@receiver(pre_delete, sender=Document, dispatch_uid='document_delete_signal')
def entry_deletion_images_delete(sender, instance, using, **kwargs):
    for key, value in instance.imageSizes.items():
        name_of_image_field = str(getattr(instance, key))  # Converts to string, if not is object itself
        os.remove(instance.baseDir + name_of_image_field)
        setattr(instance, key, None)

So what is the problem ? Should I import something more in there ? Or should I import this file somewhere ?

like image 832
Robert Brax Avatar asked Jan 22 '16 13:01

Robert Brax


2 Answers

The problem is that if you put it in a signals.py (as recommended) but do nothing more, then nobody will be importing that file.

You should follow this advice, in particular

In practice, signal handlers are usually defined in a signals submodule of the application they relate to. Signal receivers are connected in the ready() method of your application configuration class. If you’re using the receiver() decorator, simply import the signals submodule inside ready().

The ready() is reponsible for importing the module and thus provoking the signal "connection" and allows the signal receiver to work.


The full procedure should be:

# my_app/__init__.py

default_app_config = 'my_app.apps.ConfigForMyApp'

and the apps.py should be:

# my_app/apps.py

from django.apps import AppConfig

class ConfigForMyApp(AppConfig):
    # Optionally add `name` and `verbose_name`
    # for the app
    def ready(self): # overriding the ready method
        # This will trigger the @receiver decorator 
        # and thus connect the signals
        import my_app.signals
like image 107
MariusSiuram Avatar answered Nov 04 '22 02:11

MariusSiuram


Though the answer provided by MariusSiuram is the new recommended way for django >= 1.7, here is another pattern which would work irrespective of the version of django:

In your models.py file:

from django.db.models.signals import pre_delete
from myapp.signals import pre_delete_signal_dispatcher

pre_delete.connect(pre_delete_signal_dispatcher, sender=MyModel, dispatch_uid="delete_signal_dispatch")

This way, the signals are connected when models.py file is parse, and triggered on pre_delete event, and you can continue to grow your signals.py file. But yes, if the number of signal connections grow, it is easier to manage them in the app config.

like image 41
karthikr Avatar answered Nov 04 '22 01:11

karthikr