SignalerLink to this heading

Django innehåller en ”signal dispatcher” som hjälper frikopplade applikationer att få meddelanden när åtgärder inträffar någon annanstans i ramverket. I ett nötskal tillåter signaler vissa avsändare att meddela en uppsättning mottagare att någon åtgärd har ägt rum. De är särskilt användbara när många kodstycken kan vara intresserade av samma händelser.

En tredjepartsapp kan t.ex. registrera sig för att få meddelanden om ändringar i inställningarna:

Code
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)

Djangos inbyggda signaler låter användarkoden få meddelande om vissa åtgärder.

Du kan också definiera och skicka dina egna anpassade signaler. Se definiera-och-sända-signaler nedan.

Lyssna på signalerLink to this heading

För att ta emot en signal registrerar du en mottagarfunktion med metoden Signal.connect(). Mottagarfunktionen anropas när signalen skickas. Alla signalens mottagarfunktioner anropas en i taget, i den ordning de registrerades.

Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)Link to this definition
Parametrar:
  • receiver – Den callback-funktion som kommer att anslutas till denna signal. Se Mottagarens funktioner för mer information.

  • sender – Anger en viss avsändare att ta emot signaler från. Se Anslutning till signaler som skickas av specifika avsändare för mer information.

  • weak – Django lagrar signalmottagare som svaga referenser som standard. Om mottagaren är en lokal funktion kan den därför skräpsamlas. För att förhindra detta skickar du weak=False när du anropar signalens metod connect().

  • dispatch_uid – En unik identifierare för en signalmottagare i de fall där dubbla signaler kan skickas. Se Förhindra dubblerade signaler för mer information.

Låt oss se hur detta fungerar genom att registrera en signal som anropas efter varje HTTP-begäran är klar. Vi kommer att ansluta till request_finished-signalen.

Mottagarens funktionerLink to this heading

Först måste vi definiera en mottagarfunktion. En mottagare kan vara vilken Python-funktion eller metod som helst:

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

Observera att funktionen tar ett argument sender tillsammans med jokerteckennyckelordsargument (**kwargs); alla signalmottagare måste ta dessa argument.

Vi tittar på avsändare lite senare, men just nu ska du titta på argumentet **kwargs. Alla signaler skickar nyckelordsargument och kan ändra dem när som helst. För request_finished dokumenteras att inga argument skickas, vilket kan fresta oss att skriva vår signalhantering som my_receiver(sender).

Detta skulle vara fel - i själva verket kommer Django att kasta ett fel om du gör det. Det beror på att argument när som helst kan läggas till i signalen och din mottagare måste kunna hantera dessa nya argument.

Mottagare kan också vara asynkrona funktioner, med samma signatur men deklarerade med async def:

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

Signaler kan skickas antingen synkront eller asynkront och mottagarna kommer automatiskt att anpassas till rätt anropsstil. Se Skicka signaler för mer information.

Ansluta mottagarfunktionerLink to this heading

Det finns två sätt att ansluta en mottagare till en signal. Du kan välja den manuella anslutningsvägen:

Code
from django.core.signals import request_finished

request_finished.connect(my_receiver)

Alternativt kan du använda en receiver()-dekorator:

receiver(signal, **kwargs)Link to this definition
Parametrar:
  • signal – En signal eller en lista med signaler att ansluta en funktion till.

  • kwargs – Argument med nyckelord med jokertecken att skicka till en funktion.

Så här kommer du i kontakt med inredaren:

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


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

Nu anropas funktionen my_receiver varje gång en begäran avslutas.

Anslutning till signaler som skickas av specifika avsändareLink to this heading

Vissa signaler skickas många gånger, men du kommer bara att vara intresserad av att ta emot en viss delmängd av dessa signaler. Tänk till exempel på django.db.models.signals.pre_save-signalen som skickas innan en modell sparas. För det mesta behöver du inte veta när någon modell sparas - bara när en specifik modell sparas.

I dessa fall kan du registrera dig för att ta emot signaler som endast skickas av vissa avsändare. I fallet med django.db.models.signals.pre_save kommer avsändaren att vara den modellklass som sparas, så du kan ange att du bara vill ha signaler som skickas av vissa modeller:

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

Funktionen my_handler anropas endast när en instans av MyModel sparas.

Olika signaler använder olika objekt som sina avsändare; du måste konsultera inbyggd signaldokumentation för detaljer om varje enskild signal.

Förhindra dubblerade signalerLink to this heading

När dispatch_uid inte anges identifierar Django varje mottagare med Python-objektets identitet och registrerar den bara en gång. För funktioner på modulnivå, statiska metoder och klassmetoder är identiteten stabil, så att ansluta samma mottagare mer än en gång har ingen effekt:

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


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

Bundna metoder som tar ett argument self är annorlunda. Deras identitet är knuten till den specifika instansen, så att ansluta samma metod från en ny instans registrerar den som en ytterligare mottagare:

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


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

När en bunden metod används som mottagare kan flera registreringar förhindras genom att ange ett unikt dispatch_uid. Identifieraren är normalt en sträng, men vilket hashbart objekt som helst räcker. Mottagaren binds till signalen endast en gång för varje unikt dispatch_uid-värde:

Code
from django.core.signals import request_finished

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

Definiera och skicka signalerLink to this heading

Dina applikationer kan dra nytta av signalinfrastrukturen och tillhandahålla sina egna signaler.

Definiera signalerLink to this heading

class SignalLink to this definition

Alla signaler är django.dispatch.Signal-instanser.

Till exempel:

Code
import django.dispatch

pizza_done = django.dispatch.Signal()

Detta deklarerar en pizza_done signal.

Sänder signalerLink to this heading

Det finns två sätt att skicka signaler synkront i Django.

Signal.send(sender, **kwargs)Link to this definition
Signal.send_robust(sender, **kwargs)Link to this definition

Signaler kan också skickas asynkront.

Signal.asend(sender, **kwargs)Link to this definition
Signal.asend_robust(sender, **kwargs)Link to this definition

För att skicka en signal anropar du antingen Signal.send(), Signal.send_robust(), await Signal.asend() eller await Signal.asend_robust(). Du måste ange argumentet sender (som oftast är en klass) och kan ange så många andra nyckelordsargument som du vill.

Så här kan det till exempel se ut när vi skickar vår signal pizza_done:

Code
class PizzaStore:
    ...

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

Alla fyra metoderna returnerar en lista med tupelpar [(receiver, response), ...], som representerar listan över anropade mottagarfunktioner och deras svarsvärden.

send() skiljer sig från send_robust() i hur undantag som skapas av mottagarfunktioner hanteras. send() fångar inte upp några undantag som tas upp av mottagare; den tillåter helt enkelt att fel sprids. Därför kan det hända att inte alla mottagare får meddelande om en signal när det uppstår ett fel.

send_robust() fångar upp alla fel som härrör från Pythons klass Exception och ser till att alla mottagare meddelas om signalen. Om ett fel inträffar returneras felinstansen i tuple-paret för den mottagare som orsakade felet.

Spårningarna finns i attributet __traceback__ i de fel som returneras när man anropar end_robust().

asend() liknar send(), men det är en coroutine som måste inväntas:

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

Oavsett om de är synkrona eller asynkrona anpassas mottagare korrekt efter om send() eller asend() används. Synkrona mottagare anropas med sync_to_async() när de anropas via asend(). Asynkrona mottagare anropas med async_to_sync() när de anropas via send(). Precis som i fallet med middleware finns en liten prestandakostnad för att anpassa mottagare på detta sätt. För att minska antalet växlingar mellan synkrona och asynkrona anropsstilar i ett anrop till send() eller asend() grupperas mottagarna efter om de är asynkrona innan de anropas. Det innebär att en asynkron mottagare som registrerats före en synkron mottagare kan köras efter den synkrona mottagaren. Dessutom körs asynkrona mottagare parallellt med asyncio.TaskGroup.

Alla inbyggda signaler, utom de som ingår i den asynkrona request-response-cykeln, skickas med Signal.send().

Bortkoppling av signalerLink to this heading

Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)Link to this definition

För att koppla bort en mottagare från en signal, anropa Signal.disconnect(). Argumenten är de som beskrivs i Signal.connect(). Metoden returnerar True om en mottagare har kopplats bort och False om så inte är fallet. När sender skickas som en latent referens till <app label>.<model>, returnerar denna metod alltid None.

Argumentet receiver anger den registrerade mottagare som ska kopplas bort. Det kan vara None om dispatch_uid används för att identifiera mottagaren.