web-dev-qa-db-ja.com

Signals.pyファイルをDjangoプロジェクトに保存する適切な場所

私が読んでいた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内に信号もインポートするため、このコードは実行されません。

誰も私がこの問題を克服する方法についてアドバイスできますか?

よろしく

75
Mo J. Mughrabi

元の答え、Django <1.7:

アプリの_signals.py_ファイルに___init__.py_をインポートして、信号を登録できます。

_# __init__.py
import signals
_

これにより、循環インポートエラーなしで_models.py_から_signals.py_をインポートできます。

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

関連する議論

編集:Django> = 1.7:

AppConfigが導入されて以来、信号をインポートする推奨される方法はinit()関数にあります。詳細については、 Eric Marcosの回答 を参照してください。

61
yprez

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

Django(> = 1.7)の将来のバージョンでは、 推奨 方法は、アプリの設定でシグナルモジュールをインポートすることです ready() 関数:

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'
174
Eric Marcos

問題を解決するには、モデル定義の後にsignals.pyをインポートするだけです。それで全部です。

24
Kamagatos

また、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)))

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

5
aisbaa

古い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という名前のモデル内の別のフォルダー内のsignals.pyhandlers.pyで信号を分割することもできますが、私にとっては、これはエンジニアリングを超えています。 信号の配置 をご覧ください

4
Tyson Rodez

私はあなたがそうしていると推測しているので、あなたの信号は登録され、どこかで見つかるようになります。私は通常、models.pyファイルに信号を入れます。

3
Issac Kelly

別の方法は、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)

追伸:signals.pyYourModelをインポートすると、再帰が作成されます。代わりにsenderを使用してください。

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

1
Rafael

これは、別の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',
    # ...
]
0
Xen_mar