156

我刚刚开始在 django 项目中实现信号侦听器。虽然我了解它们是什么以及如何使用它们。我很难弄清楚我应该把它们放在哪里。来自 django 网站的文档是这样说的:

这段代码应该放在哪里?

您可以将信号处理和注册代码放在您喜欢的任何地方。但是,您需要确保尽早导入它所在的模块,以便在需要发送任何信号之前注册信号处理。这使您的应用程序的 models.py 成为注册信号处理程序的好地方。

虽然这是一个很好的建议,但在我的 models.py 中包含非模型类或方法只会让我犯错。

那么,存储和注册信号处理程序的最佳实践/规则是什么?

4

7 回答 7

264

这是在Django 1.7发布时添加到文档中的:

严格来说,信号处理和注册代码可以放在任何你喜欢的地方,尽管建议避免应用程序的根模块及其模型模块,以尽量减少导入代码的副作用。

在实践中,信号处理程序通常定义在与它们相关的应用程序的信号子模块中。信号接收器连接在应用程序配置类的 ready() 方法中。如果您使用的是 receiver() 装饰器,只需在 ready() 中导入信号子模块。

Django 1.7 中的更改:由于以前版本的 Django 中不存在 ready(),因此信号注册通常发生在模型模块中。

最佳实践是在信号子模块中的 handlers.py 中定义您的处理程序,例如一个看起来像这样的文件:

yourapp/signals/handlers.py

from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel

@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
    pass

注册信号处理程序的最佳位置是在定义它的应用程序的 AppConfig 中,使用ready()方法。这将如下所示:

你的应用程序/apps.py

from django.apps import AppConfig

class TasksConfig(AppConfig):
    name = 'tasks'
    verbose_name = "Tasks"

    def ready(self):
        import yourproject.yourapp.signals.handlers #noqa

确保通过直接在 settings.py 的 INSTALLED_APPS 或__init__应用程序中指定来加载 AppConfig。有关更多信息,请参阅 ready() 文档

注意:如果您也为其他应用程序提供信号来监听,请将它们放在__init__您的信号模块中,例如一个看起来像这样的文件:

yourapp/signals/__init__.py

import django.dispatch

task_generate_pre_save = django.dispatch.Signal(providing_args=["task"])

然后,另一个应用程序可以通过导入和注册来收听您的信号,例如from yourapp.signals import task_generate_pre_save. 将信号与处理程序分开可以保持干净。

Django 1.6 的说明:

如果您仍然停留在 Django 1.6 或更低版本,那么您会做同样的事情(在 yourapp/signals/handlers.py 中定义您的处理程序),但不是使用 AppConfig,而是通过 __init__.py 加载处理程序您的应用程序,例如:

你的应用程序/__init__.py

import signals

这不如使用 ready() 方法好,因为它经常导致循环导入问题。

于 2014-04-07T23:08:29.210 回答
40

我刚刚遇到这个,因为我的信号与模型无关,我想我会添加我的解决方案。

我正在记录登录/注销周围的各种数据,并且需要连接到django.contrib.auth.signals.

我已将信号处理程序放入一个signals.py文件中,然后从__init__.py模块文件中导入信号,因为我相信它会在应用程序启动时立即调用(使用print语句进行测试表明它甚至在读取设置文件之前就被调用了。)

# /project/__init__.py
import signals

在signals.py中

# /project/signals.py
from django.contrib.auth.signals import user_logged_in

def on_logged_in(sender, user, request, **kwargs):
    print 'User logged in as: \'{0}\''.format(user)

user_logged_in.connect(on_logged_in)

我对 Django (/python) 很陌生,所以我愿意接受任何人告诉我这是一个糟糕的主意!

于 2012-07-14T12:55:25.897 回答
40

我实际上喜欢让它们成为模型本身的类方法。这将所有内容都放在一个类中,这意味着您不必担心导入任何内容。

于 2010-04-27T08:14:31.767 回答
13

我最近刚刚阅读了这篇关于布局项目/应用程序的最佳实践的文章,它建议你所有的自定义调度程序信号都应该放在一个名为signals.py. 但是,这并不能完全解决您的问题,因为您仍然需要在某处导入它们,并且越早导入它们越好。

模型建议是一个很好的建议。由于您已经在signals.py文件中定义了所有内容,因此文件顶部不应超过一行。这类似于admin.py文件的布局方式(顶部是类定义,底部是注册所有自定义管理类的代码),如果您定义了信号,则将它们连接到同一个文件中。

希望有帮助!最终归结为你喜欢什么。

于 2010-04-27T07:36:20.913 回答
9

每个应用程序中的 models.py 和 signals.py 都是推荐的连接信号的地方,但是,在我看来,它们并不是保持信号和处理程序调度的最佳解决方案。调度应该是在 django 中发明信号和处理程序的原因。

苦苦挣扎了很久,终于找到了解决办法。

在 app 文件夹中创建一个连接器模块

所以我们有:

app/
    __init__.py
    signals.py
    models.py
    connectors.py

在 app/connectors.py 中,我们定义了信号处理程序并连接它们。提供了一个示例:

from signals import example_signal
from models import ExampleModel
from django.db.models.signals import post_save, post_delete

def hanndler(sender, *args, **kwargs):
    pass

post_save.connect(hander, sender=ExampleModel)

然后在 models.py 中,我们在文件末尾添加以下行:

from app import connector

一切都在这里完成。

这样,我们就可以将信号放在signals.py中,将所有的handlers放在connectors.py中。模型和信号没有混乱。

希望它提供另一种解决方案。

于 2013-05-05T06:55:01.313 回答
4

小提醒一下AppConfig。不要忘记设置:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
于 2018-08-13T11:02:24.740 回答
3

我将它们保存在一个单独的文件signals.py中,在models.py定义完所有模型之后。我导入它们并将模型连接到信号。

信号.py

#  necessary imports

def send_mail_on_save(<args>):
    # code here 

模型.py

# imports
class mymodel(models.Model):
    # model here

# import signals
from signals import send_mail_on_save
# connect them 
post_save.connect(send_mail_on_save,sender=mymodel)

这为我提供了逻辑上的分离,当然将它们保存在models.py中没有错 ,但这种方式更易于管理。

希望这可以帮助!!

于 2016-08-25T12:01:31.287 回答