# Django 6.0.7 ドキュメント

# シグナル

Django には "シグナルディスパッチャ" があり、フレームワークの他の場所でアクションが発生したときに、ほかのアプリケーションが通知を受けるのを助けてくれます。簡単に言うと、シグナルは特定の 送り手 が、あるアクションが発生したことを一連の 受け手 に通知できるようにします。特に、多くのコードが同じイベントに関連している場合に便利です。

例えば、サードパーティのアプリを登録して、設定変更の通知を受けることができます:

    from django.apps import AppConfig
    from django.core.signals import setting_changed

    def my_receiver(sender, **kwargs):
        print("Setting changed!")

    class MyAppConfig(AppConfig):
        ...

        def ready(self):
            setting_changed.connect(my_receiver)

Django の 組み込みのシグナル は、ユーザコードに特定のアクションを通知します。

また、独自のカスタムシグナルを定義して送信することもできます。以下の シグナルの定義と送信 を参照してください。

警告

シグナルは疎結合のように見えますが、すぐに理解や調整、デバッグが難しいコードにつながります。

可能であれば、シグナルでディスパッチするのではなく、処理コードを直接呼び出すことを選ぶべきです。

## シグナルを待ち受ける

シグナルを受信するには、 `Signal.connect()` メソッドを使って receiver 関数を登録します。シグナルが送信されると、レシーバ関数が呼び出されます。シグナルのすべてのレシーバ関数は、登録された順番に1つずつ呼び出されます。

Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)
  パラメータ:

    - receiver -- このシグナルに接続されるコールバック関数です。詳しくは レシーバ関数 を参照してください。

    - sender -- シグナルを受信する送信者を指定します。詳しくは 特定の送信者によって送られたシグナルに接続する を参照してください。

    - weak -- Django stores signal receivers as weak references by default. Thus, if your receiver is a local function, it may be garbage collected. To prevent this, pass `weak=False` when you call the signal's `connect()` method.

    - dispatch_uid -- シグナルが重複して送信される可能性がある場合の、シグナル受信機の一意な識別子。詳しくは 重複したシグナルを防止する を参照してください。

HTTP リクエストが終了するたびに呼び出されるシグナルを登録することで、この仕組みを見てみましょう。ここでは `request_finished` シグナルに接続します。

### レシーバ関数

まず、レシーバ関数を定義する必要があります。レシーバはPythonの関数やメソッドであれば何でもかまいません:

    def my_receiver(sender, **kwargs):
        print("Request finished!")

Notice that the function takes a `sender` argument, along with wildcard keyword arguments (`**kwargs`); all signal receivers must take these arguments.

We'll look at senders a bit later, but right now look at the `**kwargs` argument. All signals send keyword arguments, and may change those keyword arguments at any time. In the case of `request_finished`, it's documented as sending no arguments, which means we might be tempted to write our signal handling as `my_receiver(sender)`.

これは間違いです。実際、そうすると Django はエラーを返します。というのも、シグナルに引数が追加される可能性があり、レシーバはその新しい引数を扱えなければならないからです。

レシーバーは同じシグネチャーを持ち、`async def` を使って宣言された非同期関数になることもできます:

    async def my_receiver(sender, **kwargs):
        await asyncio.sleep(5)
        print("Request finished!")

シグナルは同期でも非同期でも送ることができ、受信側は自動的に正しいコールスタイルに合わせられます。詳細は シグナルを送る を参照してください。

### レシーバ関数に接続する

受信機を信号に接続する方法は2つあります。手動で接続する方法です:

    from django.core.signals import request_finished

    request_finished.connect(my_receiver)

あるいは、 `receiver()` デコレータを使うこともできます:

receiver(signal, **kwargs)
  パラメータ:

    - signal -- 関数を接続するシグナルまたはシグナルのリスト。

    - kwargs -- 関数 に渡すワイルドカードキーワード引数です。

下記がデコレーターとの繋げ方です:

    from django.core.signals import request_finished
    from django.dispatch import receiver

    @receiver(request_finished)
    def my_receiver(sender, **kwargs):
        print("Request finished!")

Now, our `my_receiver` function will be called each time a request finishes.

コードはどこに置くの？

厳密には、シグナル処理と登録のコードは好きな場所に置くことができますが、コードのインポートによる副作用を最小限にするために、アプリケーションのルートモジュールと `models` モジュールの置くのは避けることを推奨します。

In practice, signal receivers are usually defined in a `signals` submodule of the application they relate to. Signal receivers are connected in the `ready()` method of your application configuration class. If you're using the `receiver()` decorator, import the `signals` submodule inside `ready()`, this will implicitly connect signal receivers:

    from django.apps import AppConfig
    from django.core.signals import request_finished

    class MyAppConfig(AppConfig):
        ...

        def ready(self):
            # Implicitly connect signal receivers decorated with @receiver.
            from . import signals

            # Explicitly connect a signal handler.
            request_finished.connect(signals.my_receiver)

注釈

The `ready()` method may be executed more than once during testing, so you may want to guard your signals from duplication if your receiver is a bound method on an instance that may be recreated.

### 特定の送信者によって送られたシグナルに接続する

シグナルの中には何度も送信されるものがありますが、そのようなシグナルの特定のサブセットだけを受信したいと思うかも知れません。例えば、モデルが保存される前に送られるシグナル `django.db.models.signals.pre_save` を考えてみましょう。ほとんどの場合、 どの モデルが保存されるかを知る必要はありません。ある 特定の モデルが保存されたときだけ知る必要があります。

このような場合、特定の送信者のみが送信するシグナルを受信するように登録できます。 `django.db.models.signals.pre_save` の場合、送信者は保存されるモデルクラスになるので、あるモデルから送信されるシグナルだけが欲しいことを示すことができます:

    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): ...

`my_handler` 関数は `MyModel` のインスタンスが保存されたときにだけ呼び出されます。

異なるシグナルは異なるオブジェクトを送信元として使用します。それぞれのシグナルの詳細については 組み込みシグナルのドキュメント を参照する必要があります。

### 重複したシグナルを防止する

When `dispatch_uid` is not provided, Django identifies each receiver using its Python object identity and registers it only once. For module-level functions, static methods, and class methods, the identity is stable, so connecting the same receiver more than once has no effect:

    def my_handler(sender, **kwargs): ...

    my_signal.connect(my_handler)  # Running this code again is a no-op.

Bound methods, which take a `self` argument, are different. Their identity is tied to the specific instance, so connecting the same method from a new instance registers it as an additional receiver:

    def connect_signals():
        backend = Backend()
        my_signal.connect(backend.my_handler)  # A distinct receiver.

    connect_signals()  # Running this code again registers another receiver.

When using a bound method as a receiver, multiple registrations can be prevented by supplying a unique `dispatch_uid`. This identifier will usually be a string, although any hashable object will suffice. The receiver will only be bound to the signal once for each unique `dispatch_uid` value:

    from django.core.signals import request_finished

    request_finished.connect(my_receiver, dispatch_uid="my_unique_identifier")

## シグナルの定義と送信

アプリケーションは信号インフラを利用し、独自の信号を提供できます。

カスタムシグナルを使うべきタイミング

シグナルは暗黙の関数呼び出しなので、デバッグが難しくなります。カスタムシグナルの送信側と受信側の両方がプロジェクト内にある場合は、明示的な関数呼び出しを使ったほうがよいでしょう。

### シグナルを定義する

class Signal

すべてのシグナルは `django.dispatch.Signal` インスタンスです。

例:

    import django.dispatch

    pizza_done = django.dispatch.Signal()

このコードは、`pizza_done` シグナルを宣言しています。

### シグナルを送信する

Django でシグナルを同期的に送信する方法は2つあります。

Signal.send(sender, **kwargs)

Signal.send_robust(sender, **kwargs)

シグナルは非同期に送信することもできます。

Signal.asend(sender, **kwargs)

Signal.asend_robust(sender, **kwargs)

シグナルを送信するには、`Signal.send()`、`Signal.send_robust()`、`await Signal.asend()`、`await Signal.asend_robust()` のどれかを呼び出します。引数として `sender` (ほとんどの場合クラス) を指定する必要があり、他のキーワード引数を好きなだけ指定できます。

たとえば、`pizza_done` シグナルを送信するには、次のようにします:

    class PizzaStore:
        ...

        def send_pizza(self, toppings, size):
            pizza_done.send(sender=self.__class__, toppings=toppings, size=size)
            ...

4つのメソッドはすべて、呼び出されたレシーバー関数とそのレスポンス値のリストを表すタプルのペア `[(receiver, response), ...]` のリストを返します。

`send()` は `send_robust()` と異なり、レシーバ関数が発生させた例外をどのようにハンド リングするかという点で異なります。 `send()` はレシーバが発生させた例外をキャッチしません。そのため、エラーが発生してもすべてのレシーバにシグナルが通知されるとは限りません。

`send_robust()` は Python の `Exception` クラスに由来するすべてのエラーをキャッチし、すべてのレシーバにシグナルが通知されるようにします。エラーが発生した場合、エラーを発生させたレシーバのタプルのペアにエラーインスタンスが返されます。

トレースバックは `send_robust()` を呼び出したときに返されるエラーの `__traceback__` 属性に存在します。

`asend()` は `send()` と似ていますが、await しなければならないコルーチンです:

    async def asend_pizza(self, toppings, size):
        await pizza_done.asend(sender=self.__class__, toppings=toppings, size=size)
        ...

Whether synchronous or asynchronous, receivers will be correctly adapted to whether `send()` or `asend()` is used. Synchronous receivers will be called using `sync_to_async()` when invoked via `asend()`. Asynchronous receivers will be called using `async_to_sync()` when invoked via `send()`. Similar to the case for middleware, there is a small performance cost to adapting receivers in this way. Note that in order to reduce the number of sync/async calling-style switches within a `send()` or `asend()` call, the receivers are grouped by whether or not they are async before being called. This means that an asynchronous receiver registered before a synchronous receiver may be executed after the synchronous receiver. In addition, async receivers are executed concurrently using `asyncio.TaskGroup`.

非同期のリクエスト/レスポンス・サイクル以外のすべての組み込みシグナルは `Signal.send()` を使ってディスパッチされます。

Changed in Django 6.1:

In older versions, async receivers were executed via `asyncio.gather()`.

## シグナルを切断する

Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)

シグナルからレシーバーを切断するには `Signal.disconnect()` を呼び出します。引数は `Signal.connect()` の説明と同じです。このメソッドは、レシーバが切断された場合は `True` を、切断されなかった場合は `False` を返します。`sender` が `<app label>.<model>` への遅延参照として渡された場合、このメソッドは常に `None` を返します。

引数 `receiver` は、切断する登録済みのレシーバを指定します。レシーバを識別するために `dispatch_uid` を使用する場合は `None` を指定します。