信号处理程序应该放在django项目中的哪个位置?

Jas*_*ebb 135 django signal-handling

我刚刚开始在django项目中实现信号监听器.虽然我明白它们是什么以及如何使用它们.我很难搞清楚应该放在哪里.来自django网站的文档有这样的说法:

这段代码应该在哪里生活?

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

虽然这是一个很好的建议,但在我的models.py中使用非模型类或方法只会让我误以为然.

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

Aid*_*dan 230

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
Run Code Online (Sandbox Code Playgroud)

注册信号处理程序的最佳位置是使用ready()方法定义它的应用程序的AppConfig .这将是这样的:

yourapp/apps.py:

from django.apps import AppConfig

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

    def ready(self):
        import yourproject.yourapp.signals.handlers #noqa
Run Code Online (Sandbox Code Playgroud)

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

注意:如果您为其他应用程序提供信号也要收听,请将它们放入__init__信号模块中,例如文件看起来像:

yourapp /信号/ __ init__.py

import django.dispatch

task_generate_pre_save = django.dispatch.Signal(providing_args=["task"])
Run Code Online (Sandbox Code Playgroud)

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

Django 1.6的说明:

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

yourapp/__ init__.py

import signals
Run Code Online (Sandbox Code Playgroud)

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

  • 正如文档说你覆盖准备好了,你可能想做像super(ReportsConfig,self).ready()这样的事情,以防django决定用某些东西填充ready()(截至1.7.0它当前是空的) (3认同)
  • 我认为这个答案是最好的,因为它是唯一一个解决进口副作用的人.我来到这里寻找最佳实践,因为我正在清理一个应用程序,由于这种副作用而被打破.唉应用程序在django 1.6上运行,最佳实践仅适用于django 1.7.让`__init__`导入信号的临时解决方法对我来说不起作用,所以我想知道是否还有其他地方我可以导入信号,直到我们准备好升级到以后的django版本. (2认同)
  • fwiw 我不需要 TaskConfig 类代码块的最后一行中的 `yourproject.`。我已经使用这种结构进行了这项工作,因此请考虑这个 qa :) (2认同)

Dan*_*man 39

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

  • 如果您正在聆听该模型发出的信号,那么将所有听众放在那里也会使整个练习毫无意义,不是吗?信号点是解耦的.听众不应该使用对这些远程事件感兴趣的代码吗?问题是如何确保在发射器之前加载侦听器. (96认同)
  • 您通常将处理程序连接到信号的哪个位置? (2认同)
  • 对John Mee来说,它与覆盖save()等没什么不同. (2认同)

Hug*_*own 39

我只是碰到了这个,因为我的信号与模型无关,我想我会添加我的解决方案.

我正在登录/注销各种数据,并需要挂钩django.contrib.auth.signals.

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

# /project/__init__.py
import signals
Run Code Online (Sandbox Code Playgroud)

并在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)
Run Code Online (Sandbox Code Playgroud)

我是Django(/ python)的新手,所以任何人都可以告诉我这是一个糟糕的主意!

  • 这感觉合乎逻辑,但我建议在应用程序级别进行. (3认同)
  • 小心,这种逻辑很可能会导致重复的信号被触发.`user_logged_in.connect(on_logged_in)`应该最有可能传入`dispatch_uid`参数.更多信息,请访问https://docs.djangoproject.com/en/dev/topics/signals/#preventing-duplicate-signals. (2认同)

hor*_*ora 13

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

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

希望有所帮助!归根结底,它取决于你喜欢什么.

  • 那里有一个交叉导入:signals.py尝试从models.py导入模型 (9认同)

sam*_*uel 7

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

我挣扎了很长时间,最后我们找到了解决方案.

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

所以我们有:

app/
    __init__.py
    signals.py
    models.py
    connectors.py
Run Code Online (Sandbox Code Playgroud)

在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)
Run Code Online (Sandbox Code Playgroud)

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

from app import connector
Run Code Online (Sandbox Code Playgroud)

一切都在这里完成.

通过这种方式,我们可以将信号放在signals.py中,并将所有处理程序放在connectors.py中.模型和信号没有混乱.

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


val*_*lex 6

关于 的小提醒AppConfig。不要忘记设置:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
Run Code Online (Sandbox Code Playgroud)