Existem várias maneiras de resolver o problema que você descreveu:
- Lógica do aplicativo
- Lógica específica da visualização -- Se o comportamento for específico de uma única visualização, coloque as alterações na visualização.
- Lógica específica do modelo -- Se o comportamento for específico para um único modelo, substituir o método save() para o modelo.
- Lógica de middleware -- Se o comportamento estiver relacionado a vários modelos OU precisar envolver um aplicativo existente, você pode usar o sinais pré-salvar/pós-salvar para adicionar comportamentos adicionais sem alterar o próprio aplicativo.
- Procedimentos armazenados do banco de dados -- Normalmente uma possibilidade, mas o ORM do Django não os usa. Não é portátil entre bancos de dados.
- Acionadores de banco de dados -- Não é portátil de um banco de dados para outro (ou mesmo uma versão de um banco de dados para outra), mas permite que você controle o comportamento compartilhado em vários aplicativos (possivelmente não-Django).
Pessoalmente, prefiro usar sobrescrever o método save() ou usar um sinal Django. O uso de lógica específica de exibição pode pegá-lo em aplicativos grandes com várias exibições do(s) mesmo(s) modelo(s).