ForeignKeys anuláveis ​​e excluindo uma instância modelo referenciado

votos
7

Eu tenho um ForeignKey que pode ser nulo no meu modelo para modelar um baixo acoplamento entre os modelos. Parece um pouco como que:

class Message(models.Model):
  sender = models.ForeignKey(User, null=True, blank=True)
  sender_name = models.CharField(max_length=255)

Em salvar o nome do remetente é escrito para o atributo sender_name. Agora, eu quero ser capaz de excluir a instância de usuário referenciado pelo remetente e deixar a mensagem no lugar.

Fora da caixa, este código sempre resulta em mensagens apagadas assim que eu excluir a instância de usuário. Então eu pensei que um manipulador de sinal seria uma boa ideia.

def my_signal_handler(sender, instance, **kwargs):
  instance.message_set.clear()
pre_delete.connect(my_signal_handler, sender=User)

Infelizmente, é de nenhuma maneira uma solução. De alguma forma Django primeiro recolhe o que quer excluir e, em seguida, dispara o manipulador pre_delete.

Alguma ideia? Onde está o nó em meu cérebro?

Publicado 25/03/2009 em 14:06
fonte usuário
Em outras línguas...                            


2 respostas

votos
13

Django, de fato, imitar do SQL ON DELETE CASCADEcomportamento, e não há nenhuma maneira out-of-the box documentado para mudar isso. Os docs onde eles mencionam este estão perto do final desta seção: Excluindo objetos .

Está certo que Django recolhe todas as instâncias modelo relacionados, em seguida, chama o manipulador de pré-delete para cada um. O remetente do sinal será a classe modelo prestes a ser eliminada, neste caso Message, em vez de User, o que torna difícil detectar a diferença entre uma cascata apagar desencadeada por usuário e uma normal de exclusão ... especialmente desde que o sinal para apagar a classe User vem por último, uma vez que essa é a última eliminação :-)

Você pode, no entanto, obter a lista de objetos que Django está propondo para apagar antes de chamar a função User.delete (). Cada instância do modelo tem um método semi-privada chamada _collect_sub_objects()que compila a lista de instâncias com chaves estrangeiras apontando para ele (ele compila esta lista sem excluir os casos). Você pode ver como este método é chamado por olhar para delete()em django.db.base.

Se este foi um dos seus próprios objetos, eu recomendo substituindo o delete()método em sua instância para executar _collect_sub_objects (), e, em seguida, quebrar as ForeignKeys antes de chamar a classe super excluir. Desde que você está usando um built-in Django objeto que você pode achar muito difícil de subclasse (embora seja possível substituir o seu próprio objeto Usuário para Django), você pode ter que confiar na lógica vista a executar _collect_sub_objectse quebrar as FKs antes da eliminação .

Aqui está um exemplo rápido e sujo:

from django.db.models.query import CollectedObjects
u = User.objects.get(id=1)


instances_to_be_deleted = CollectedObjects()
u._collect_sub_objects(instances_to_be_deleted)

for k in instances_to_be_deleted.ordered_keys():
    inst_dict = instances_to_be_deleted.data[k]
    for i in inst_dict.values():
        i.sender = None  # You will need a more generic way for this
        i.save()

u.delete()
Respondeu 25/03/2009 em 14:31
fonte usuário

votos
0

Tendo acabado de descobrir o comportamento ON DELETE CASCADE mim, vejo que no Django 1.3 que fizeram o comportamento de chave estrangeira configurável .

Respondeu 14/04/2011 em 13:34
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more