Djangoプロジェクトでsignals.pyファイルを保持するのに適切な場所


88

私が読んでいたDjangoのドキュメントに基づくsignals.pyと、アプリフォルダーから始めるのが良いようですが、私が直面している問題は、のシグナルを作成pre_saveしてモデルからクラスをインポートしようとすると、競合することです。import私のモデルでは。

# models.py

from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext as _
from signals import *

class Comm_Queue(CommunicatorAbstract):
    queue_statuses = (
        ('P', _('Pending')),
        ('S', _('Sent')),
        ('E', _('Error')),
        ('R', _('Rejected')),
    )
    status          = models.CharField(max_length=10, db_index=True, default='P')
    is_html         = models.BooleanField(default=False)
    language        = models.CharField(max_length=6, choices=settings.LANGUAGES)
    sender_email    = models.EmailField()
    recipient_email = models.EmailField()
    subject         = models.CharField(max_length=100)
    content         = models.TextField()

# signals.py

from django.conf import settings
from django.db.models.signals import pre_save
from django.dispatch import receiver
from models import Comm_Queue

@receiver(pre_save, sender=Comm_Queue)
def get_sender_email_from_settings(sender, **kwargs):
    obj=kwargs['instance']
    if not obj.sender_email:
        obj.sender_email='%s' % settings.ADMINS[0][1]

Comm_Queue内部signals.pyをインポートし、内部のシグナルもインポートするため、このコードは実行されませんmodels.py

この問題をどのように克服できるかについて誰かがアドバイスできますか?

よろしく


回答:


65

元の回答、Django <1.7の場合:

signals.pyアプリの__init__.pyファイルにインポートすることで、シグナルを登録できます。

# __init__.py
import signals

これにより、循環インポートエラーなしでmodels.pyからsignals.pyインポートできます。

このアプローチの問題の1つは、coverage.pyを使用している場合、カバレッジ結果が台無しになることです。

関連する議論

編集:Django> = 1.7の場合:

AppConfigが導入されて以来、シグナルをインポートするための推奨される方法はそのinit()機能にあります。詳細については、 EricMarcosの回答を参照してください。


6
Django 1.9のシグナルを使用するには、以下の方法を使用します(djangoが推奨)。この方法は与えても機能しませんAppRegistryNotReady("Apps aren't loaded yet.")
s0nskar 2016

1
エリックマルコス彼の答えは受け入れられた答えであるはずです:アプリ設定を使用して、Django> = 1.7以来、stackoverflow.com / a / 21610250/3202958
Nrzonline

1
同意しました。回答を編集して、Django1.7以降に対するEricMarcosの回答を指すようにします
yprez

194

Django <= 1.6を使用している場合は、カマガトスのソリューションをお勧めします。モデルモジュールの最後にシグナルをインポートするだけです。

Djangoの将来のバージョン(> = 1.7)の場合、推奨される方法は、アプリのconfig ready()関数にsignalsモジュールをインポートすることです。

my_app/apps.py

from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        import my_app.signals

my_app/__init__.py

default_app_config = 'my_app.apps.MyAppConfig'

7
また、1.7のドキュメントでは、readyを複数回呼び出すことができるため、シグナルの重複を避けるために、シグナルコネクタ呼び出しに一意の識別子を添付することも記載されています。request_finished.connect(my_callback、dispatch_uid = "my_unique_identifier")ここで、dispatch_uidは通常文字列です。ただし、ハッシュ可能なオブジェクトであればどれでもかまいません。 docs.djangoproject.com/en/1.7/topics/signals/…–
Emeka

13
これは受け入れられた答えでなければなりません!上記の受け入れられた回答は、uwsgiを使用して展開するときにエラーをスローします
Patrick

2
うーん、django 2では機能しません。モデルを直接インポートする場合は、すべて問題ありません。モデルをシグナルにインポートし、シグナルを準備完了でインポートすると、エラーが発生しdoesn't declare an explicit app_labelます..
Aldarund 2018年

@Aldarunでは、INSTALLED_APPS内に「my_app.apps.MyAppConfig」を配置してみてください。
RamilAglyautdinov19年

26

問題を解決するには、モデル定義の後にsignals.pyをインポートする必要があります。それで全部です。


2
これははるかに簡単で、循環依存がなければこれが機能するとは思いもしませんでした。ありがとう!
bradenm 2012年

2
鮮やかさ。私の答えよりもこのように。私は本当にそれは、円形のインポートが発生することはありませんどのように来る理解して...ませんが
yprez

ソリューションは、Eclipseで有効になっているautopep8プラグインでは機能しません。
ramusus 2014

5

また、signals.pyファイルにシグナルを入れ、すべてのシグナルをロードする次のコードスニペットもあります。

# import this in url.py file !

import logging

from importlib import import_module

from django.conf import settings

logger = logging.getLogger(__name__)

signal_modules = {}

for app in settings.INSTALLED_APPS:
    signals_module = '%s.signals' % app
    try:
        logger.debug('loading "%s" ..' % signals_module)
        signal_modules[app] = import_module(signals_module)
    except ImportError as e:
        logger.warning(
            'failed to import "%s", reason: %s' % (signals_module, str(e)))

これはプロジェクト用です。アプリレベルで機能するかどうかはわかりません。


これは、他のパターン(tasks.pyなど)に適合する限り、私のお気に入りのソリューションです
dalore 2013

1
これに問題が見つかりました。シェルを起動すると、urls.pyがインポートされず、シグナルがアタッチされません
dalore 2014年

はい、私の答えはちょっと時代遅れです。最近、djangoにはAppConfigクラスがあるようです。前回djangoを使用したのは、バージョン1.3でした。その周りを調査することを提案します。
aisbaa 2014年

1
私たちはまだ1.6なので、すべてのsignals.pyをモデルに移動する必要がありました。そうしないと、セロリと管理コマンドが取得されませんでした
dalore 2014年

5

古いDjangoバージョンでは、信号を__init__.pyまたはに配置するのに問題はありません。models.py(ただし、最終的にはモデルが私の好みに合わせて大きくなります)。

Django 1.9では、シグナルをsignals.pyファイルに配置し、それらをインポートする方が良いと思います。apps.py、モデルのロード後にロードされる。

apps.py:

from django.apps import AppConfig


class PollsConfig(AppConfig):
    name = 'polls'

    def ready(self):
        from . import signals  # NOQA

あなたはまた、上の信号を分割することができますsignals.pyし、handlers.py名前のモデル内の別のフォルダにsignalsだけでなく、私にとってそれは単なる過剰性能です。信号の配置を見てください


3

信号が登録され、どこかで見つかるように、そうしていると思います。通常、信号をmodels.pyファイルに正しく配置します。


ええ、モデルファイル内で信号を移動すると、問題が解決します。しかし、私のmodel.pyファイルはかなり大きく、すべてのクラス、マネージャー、モデルルールが含まれています。
Mo J. Mughrabi 2011

1
マネージャーは私の経験を引き出すのが少し簡単です。Managers.pyftw。
Issac Kelly

3

これは、シグナルが別のsignals.pyファイルにある場合にのみ適用されます

@EricMarcosの回答に完全に同意しますが、django docsはdefault_app_config変数を使用しないように明示的にアドバイスしていることを述べておく必要があります(間違いではありませんが)。現在のバージョンの場合、正しい方法は次のとおりです。

my_app / apps.py

from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        import my_app.signals

settings.py

(インストールされているアプリにアプリ名だけでなく、AppConfigへの相対パスがあることを確認してください)

INSTALLED_APPS = [
    'my_app.apps.MyAppConfig',
    # ...
]

1

別の方法は、からコールバック関数をインポートし、signals.pyそれらを接続することですmodels.py

signals.py

def pre_save_callback_function(sender, instance, **kwargs):
    # Do stuff here

model.py

# Your imports here
from django.db.models.signals import pre_save
from yourapp.signals import pre_save_callback_function

class YourModel:
    # Model stuff here
pre_save.connect(pre_save_callback_function, sender=YourModel)

追伸:インポートYourModelするsignals.pyと再帰が作成されます。使用するsender代わりに、。

追伸:インスタンスをコールバック関数に再度保存すると、再帰が作成されます。.saveメソッドで制御引数を作成して制御できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.