Why Django model signals are not working?

DjangoDjango ModelsDjango Signals

Django Problem Overview


I am trying to create activity streams of users from their status.

models:

class Status(models.Model):
    body = models.TextField(max_length=200)
    image = models.ImageField(blank=True, null=True, upload_to=get_upload_file_name)
    privacy = models.CharField(max_length=1,choices=PRIVACY, default='F')
    pub_date = models.DateTimeField(auto_now_add=True, auto_now=False)
    user = models.ForeignKey(User)

class Activity(models.Model):
    actor = models.ForeignKey(User)
    action = models.CharField(max_length=100)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    pub_date = models.DateTimeField(auto_now_add=True, auto_now=False)

However, although I create a new status, it does not create a new activity from the post_save signal.

signals:

from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import post_save
from status.models import Status
from models import Activity

def create_activity_item(sender, instance, signal, *args, **kwargs):
    if kwargs.get('created', True):
        ctype = ContentType.objects.get_for_model(instance)

        if ctype.name == 'Status':
            action = ' shared '

            activity = Activity.objects.get_or_create(
                actor = instance.user,
                action = action,
                content_type = ctype,
                object_id = instance.id,
                pub_date = instance.pubdate
            )

post_save.connect(create_activity_item, sender=Status)

What am I doing wrong? Please help me solve this problem. I will be very much grateful. Thank you.

Update:

However doing like this creates the activity:

@receiver(post_save, sender=Status)
def create(sender, instance, **kwargs):
    if kwargs.get('created',True):
        ctype = ContentType.objects.get_for_model(instance)
        activity = Activity.objects.get_or_create(
            actor = instance.user,
            action = ' shared ',
            content_type = ctype,
            object_id = instance.id,
            pub_date = instance.pub_date
        )

Why doesn't the above works then?

Django Solutions


Solution 1 - Django

Seems like your post_save.connect is not executed. You should import signals somewhere. For django 1.7 it is recommended to do this in the app's config ready() function. Read the "Where should this code live?" side note in the docs.

For example if your app is called activity:

activity/init.py

default_app_config = 'activity.apps.ActivityAppConfig'

activity/apps.py

from django.apps import AppConfig

class ActivityAppConfig(AppConfig):
    name = 'activity'

    def ready(self):
        import activity.signals

And don't forget to add dispatch_uid to your connect() call:

post_save.connect(create_activity_item, sender=Status,
                  dispatch_uid="create_activity_item")

UPDATE: name attribute of ContentType is always in lower case. So you should change the if statement to:

if ctype.name == 'status':

Solution 2 - Django

let's say your apps name is blog, in the settings.py file of your project ensure to register the blog app in the INSTALLED_APP variable of your main project's settings.py file as blog.apps.BlogConfig and not just blog. That worked for me.

Solution 3 - Django

If you write everything in signals.py correctly but not working, then check these steps... (suppose in an app named AppName)

  1. in __init__.py, put the line

    default_app_config = 'AppName.apps.AppnameConfig'
    
  2. in apps.py file, put the block

    from django.apps import AppConfig
    
    
    class AppnameConfig(AppConfig):
        name = 'AppName'
     
        def ready(self):
            import AppName.signals
    

Solution 4 - Django

Without touching apps.py this worked for me.

class MyModel(models.Model):
    """ MyModel fields go """
    body = models.TextField(max_length=200)
    pub_date = models.DateTimeField(auto_now_add=True, auto_now=False)


def post_save_actions(sender, instance, created, **kwargs):
    if created:
        pass
        # post save actions if new instance is created,
        # do something with `instance` or another models
        # be careful about circular imports. \m/

and the signals hook,

post_save.connect(post_save_user_actions, sender=MyModel)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKakarView Question on Stackoverflow
Solution 1 - DjangocatavaranView Answer on Stackoverflow
Solution 2 - DjangoCollins KelechiView Answer on Stackoverflow
Solution 3 - Djangoabinash_269View Answer on Stackoverflow
Solution 4 - DjangoAll Іѕ VаиітyView Answer on Stackoverflow