为什么Django模型信号不起作用?

66
我正在尝试从用户的状态创建活动流。
模型:
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)

然而,虽然我创建了一个新状态,但它并没有从post_save信号中创建一个新的活动。

信号:

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)

我做错了什么?请帮助我解决这个问题。非常感激。谢谢。

更新:

然而像这样做会创建该活动:

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

为什么上述方法不起作用?

7个回答

163

看起来你的post_save.connect没有被执行。你应该在某个地方导入signals。对于Django 1.7,建议在应用程序的配置ready()函数中执行此操作。请阅读文档中的"这段代码应该放在哪里?"小节。

例如,如果你的应用程序名为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

别忘了在你的connect()调用中添加dispatch_uid

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

更新: ContentTypename 属性始终为小写。因此,您应该将 if 语句更改为:

if ctype.name == 'status':

3
你好。我按照你在答案中提供的方法做了,但仍然没有创建任何活动。在__init__.py文件中,我添加了default_app_config = 'activities.apps.ActivityAppConfig',因为activities是应用程序的名称。然后,在该应用程序中添加了一个新的apps.py文件,并将ActivityAppConfig的名称字段更改为“activities”。最后,添加了dispatch_uid - Kakar
1
好的,我会记住的!非常感谢你! - Kakar
1
这个答案应该在Django文档中。 - Jamie S
3
Django文档中有这个,但它使它看起来是可选的,你可能不知道除非所有月亮都对齐,否则什么都不会起作用。 - Dr Manhattan
1
只是想提醒一下:你的IDE(PyCharm)可能会说import activity.signals没有被使用,但不要被愚弄了,因为Django确实需要它来使信号在你的模型中工作。这个答案让我省了几个小时的压力。谢谢。 - ivanleoncz
显示剩余4条评论

19
如果您在signals.py中正确地编写了所有内容但仍无法正常工作,则请检查以下步骤...(假设在名为AppName的应用程序中)
  1. __init__.py中加入以下代码:

    default_app_config = 'AppName.apps.AppnameConfig'
    
  2. apps.py文件中,放置该代码块

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

1
这个有效了,我不需要在__init__.py中添加这些行。 - Mike D Hovhannisyan

17

假设你的应用程序名称为blog,在项目的settings.py文件中,请确保将blog应用程序注册到主项目的settings.py文件中的INSTALLED_APP变量中,作为blog.apps.BlogConfig而不是只有blog。 这对我有效。


3
有人知道这为什么有效吗? - Kwaku Biney
1
你还需要在 apps.py 中添加以下代码 >> def ready(self): import activity.signals - oruchkin

6

简单、可扩展、可重复、可重用的答案是...

如果你计划在应用程序(例如:posts)中使用信号(例如:signals.py),只需养成以下习惯,将此方法添加到你的apps.pyAppConfig类中,每次都要这样做。

def ready(self):
    from . import signals

你不需要像其他人说的那样去修改 __init__.py 文件。这样你的 signals 就可以正常工作了。


这对Django 4.2来说已经足够了 - undefined

3

在不修改 apps.py 的情况下,这个方法对我有效。

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/

和信号挂钩,
post_save.connect(post_save_user_actions, sender=MyModel)

3
注意循环导入问题,1个赞 \m/ - Ansuman

0
你应该在__init__.py文件中添加以下内容:

default_app_config = 'activity.apps.ActivityAppConfig'


0
首先尝试使用这些配置。
  1. __init__.py文件中

    default_app_config = 'AppName.apps.AppNameConfig'
    
  2. apps.py文件中

    from django.apps import AppConfig
    
    
    class AppNameConfig(AppConfig):
        name = 'AppName'
    
        def ready(self):
            from . import signals
            # import AppName.signals
    
但是如果你的应用程序仍然无法正常工作,那么不要在__init__.py文件中编写应用程序配置,而是将你的应用程序配置包含在settings.py文件中。
    INSTALLED_APPS = [
      'AppName.apps.AppNameConfig',
    ]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接