Sinais do Django vs. método de salvamento sobrescrito

89

Estou tendo problemas para entender isso. No momento, tenho alguns modelos que se parecem com este:

 def Review(models.Model)
    ...fields...
    overall_score = models.FloatField(blank=True)

def Score(models.Model)
    review = models.ForeignKey(Review)
    question = models.TextField()
    grade = models.IntegerField()

Uma revisão tem várias "pontuações", a pontuação geral é a média das pontuações. Quando uma revisão ou pontuação é salva, preciso recalcular a média da pontuação geral. No momento, estou usando um método de salvamento sobrescrito. Haveria algum benefício em usar o despachante de sinal do Django?

imjoevasquez
fonte

Respostas:

85

Os sinais de salvar / excluir são geralmente favoráveis ​​em situações em que você precisa fazer alterações que não são completamente específicas ao modelo em questão, ou podem ser aplicadas a modelos que têm algo em comum ou podem ser configurados para uso em modelos.

Uma tarefa comum em savemétodos substituídos é a geração automatizada de slugs de algum campo de texto em um modelo. Este é um exemplo de algo que, se você precisar implementá-lo para vários modelos, se beneficiaria com o uso de um pre_savesinal, onde o manipulador de sinal poderia pegar o nome do campo do slug e o nome do campo a partir do qual gerar o slug. Uma vez que você tenha algo parecido, qualquer funcionalidade aprimorada que você colocar no lugar também se aplicará a todos os modelos - por exemplo, olhando o pacote que você está prestes a adicionar para o tipo de modelo em questão, para garantir exclusividade.

Aplicativos reutilizáveis ​​geralmente se beneficiam do uso de sinais - se a funcionalidade que eles fornecem pode ser aplicada a qualquer modelo, eles geralmente (a menos que seja inevitável) não vão querer que os usuários tenham que modificar diretamente seus modelos para se beneficiar dele.

Com django-mptt , por exemplo, eu usei o pre_savesinal para gerenciar um conjunto de campos que descrevem uma estrutura de árvore para o modelo que está para ser criado ou atualizado e o pre_deletesinal para remover detalhes da estrutura de árvore para o objeto que está sendo excluído e todo o seu sub-árvore de objetos antes dele e eles são excluídos. Devido ao uso de sinais, os usuários não precisam adicionar ou modificar saveou deletemétodos em seus modelos para que esse gerenciamento seja feito para eles, eles só precisam informar ao django-mptt quais modelos desejam gerenciar.

Jonny Buchanan
fonte
E se o manipulador de sinal disparar uma exceção? Suponho que não devam disparar exceções, ou então não são um bom ajuste. Estou errado?
x-yuri
20

Você perguntou:

Haveria algum benefício em usar o despachante de sinal do Django?

Eu encontrei isso na documentação do django:

Métodos de modelo substituídos não são chamados em operações em massa

Observe que o método delete () para um objeto não é necessariamente chamado ao excluir objetos em massa usando um QuerySet ou como resultado de uma exclusão em cascata. Para garantir que a lógica de exclusão personalizada seja executada, você pode usar sinais pre_delete e / ou post_delete.

Infelizmente, não há uma solução alternativa ao criar ou atualizar objetos em massa, uma vez que nenhum save (), pre_save e post_save são chamados.

De: Substituindo métodos de modelo predefinidos

Guettli
fonte
3
A visualização da lista de administradores do Django usa exclusão em massa ... estava confuso até encontrar este boato.
N.Balauro
8
também diz "Infelizmente, não há uma solução alternativa ao criar ou atualizar objetos em massa, uma vez que nenhum save (), pre_save e post_save são chamados." - então eu não acho que isso seja uma troca entre esses métodos.
Cory
Isso se aplica a ambos os métodos, então a resposta é: "não, não há nenhum benefício em usar sinais ao invés de substituir o savemétodo"?
Flimm
3

Se você usar sinais, poderá atualizar a pontuação da revisão cada vez que o modelo de pontuação relacionado for salvo. Mas se não preciso dessa funcionalidade, não vejo razão para colocar isso em sinal, isso é muito relacionado ao modelo.

Dmitry Shevchenko
fonte
2

É uma espécie de desnormalização. Veja esta bela solução . Definição de campo de composição no local.

Alex Koshelev
fonte
1

Uma pequena adição da documentação do Django sobre exclusão em massa ( .delete()método em QuerySetobjetos):

Lembre-se de que, sempre que possível, isso será executado puramente em SQL e, portanto, os métodos delete () de instâncias de objetos individuais não serão necessariamente chamados durante o processo. Se você forneceu um método delete () personalizado em uma classe de modelo e deseja garantir que ele seja chamado, você precisará excluir "manualmente" as instâncias desse modelo (por exemplo, iterando sobre um QuerySet e chamando delete () em cada objeto individualmente) em vez de usar o método delete () em massa de um QuerySet.

https://docs.djangoproject.com/en/1.11/topics/db/queries/#deleting-objects

E atualização em massa ( .update()método em QuerySetobjetos):

Finalmente, perceba que update () faz uma atualização no nível de SQL e, portanto, não chama nenhum método save () em seus modelos, nem emite os sinais pre_save ou post_save (que são uma consequência da chamada de Model.save ( )). Se você quiser atualizar um monte de registros para um modelo que tem um método save () personalizado, faça um loop sobre eles e chame save ()

https://docs.djangoproject.com/en/2.1/ref/models/querysets/#update

valex
fonte
Isso não se aplica a ambos?
Flimm