Sinais (Signals)

O Django incluí um “dispachador de sinal” que ajuda ao permitir que aplicações dissociadas sejam notificadas quando uma ação ocorre em qualquer lugar do framework. Em resumo, sinais permitem certos remetentes notificar um conjunto de receptores sobre alguma ação que tenha ocorrido. Eles são especialmente úteis quando muitas peças de código podem estar interessados nos mesmos eventos.

O Django fornece um conjunto de sinais embutidos que deixam o código do usuário ser notificado pelo próprio Django sobre certas ações. Estas incluem algumas notificações úteis:

Veja a documentação de sinais embutidos para uma lista completa, e uma explicação completa de cada sinal.

Você também pode definir e enviar seus próprios sinais; veja abaixo.

Ouvindo sinais

Para receber um sinal, você precisa registrar uma função receptora que será chamada quando o sinal é enviado. Vamos ver como isto funciona registrando um sinal que é chamado depois de cada requisição HTTP é finalizada. Nós conectaremo-nos ao sinal request_finished.

Funções receptoras

Primeiro, nós precisamos definir uma função receptora. Um receptor pode ser qualquer função Python ou método:

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

Observe que a função recebe um argumento sender, junto com o argumento coringa (**kwargs); todo manipulador de sinal recebe estes argumentos.

Nós olharemos o remetente um pouco depois, mas agora olhe o argumento **kwargs. Todos os sinais enviam argumentos nomeados, e podem mudar seus argumentos nomeados a qualquer momento. Neste caso o request_finished, ele está documentado como se não enviasse argumentos, que significa que poderiámos ser tentados a escrever nosso manipulador do sinal como my_callback(sender).

Isso poderia estar errado – de fato, o Django irá lançar um erro se você fizer isso. Isto porque, em qualquer ponto, argumentos poderiam ser adicionados ao sinal, e o receptor deve ser capaz de manipular estes argumentos.

Conectando funções receptoras

Agora, nós precisaremos conectar nosso receptor ao sinal:

from django.core.signals import request_finished

request_finished.connect(my_callback)

Agora, nossa função my_callback será chamada a cada vez que uma requisição for finalizada.

Onde este código deve ficar?

Você poder colocar o código manipulador e de registro de sinal aonde você quiser. Entretanto, você precisará assegurar-se de que o módulo já tenha sido importado anteriormente, para que este manipulador de sinal seja registrado antes que qualquer sinal seja enviado. Isto faz do models.py de sua aplicação, um bom lugar para colocar o regsitro de manipuladores de sinais.

Conectando-se a sinais enviados por remetentes específicos

Alguns sinais são enviados muitas vezes, mas você somente é interessado em receber um certo sub-conjunto destes sinais. Por exemplo, considere o sinal django.db.models.signals.pre_save enviado antes de um model ser salvo. Na maioria das vezes, você não precisa saber quando qualquer model foi salvo – só quando um model específico é salvo.

Nestes casos, você pode registrá-lo para receber sinais enviados somente por um remetente em particular. No caso do django.db.models.signals.pre_save, o remetente será a classe model que estiver sendo salva, então você pode indicar que você somente quer sinais enviados por algum model:

from django.db.models.signals import pre_save
from myapp.models import MyModel

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

pre_save.connect(my_handler, sender=MyModel)

A função my_handler somente será chamada quando uma instância de MyModel for salva.

Sinais diferentes usam objetos diferentes como seus remetentes; você precisará consultar a documentação de sinais embutidos para detalhes de cada sinal em particular.

Definindo e enviando sinais

Sua aplicação pode tirar proveito da infraestrutura de sinais e prover seus próprios sinais.

Definindo sinais

class Signal([providing_args=list])

Todos os sinais são instâncias de django.dispatch.Signal. O providing_args é uma lista de nomes de argumentos que o sinal fornecerá aos ouvintes.

Por exemplo:

import django.dispatch

pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

Este declara um sinal pizza_done que fornecerá aos receptores os argumentos toppings e size.

Lembre-se de que você está autorizado a alterar esta lista de argumentos a qualquer momento, então obter o direito de API, na primeira tentativa, não é necessário.

Enviando sinais

Signal.send(sender, **kwargs)

Para enviar um sinal, chame Signal.send(). Você deve prover o argumento sender, e pode fornecer muitos outros argumentos nomeados conforme sua vontade.

Por exemplo, aqui mostramos como nosso envio de sinal pizza_done, pode ficar:

class PizzaStore(object):
    ...

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