o que on_delete faz nos modelos do Django?

348

Estou familiarizado com o Django, mas recentemente notei que existe uma on_delete=models.CASCADEopção com os modelos. Procurei a documentação para o mesmo, mas não consegui encontrar nada além de:

Alterado no Django 1.9:

on_deleteagora pode ser usado como o segundo argumento posicional (anteriormente, normalmente era passado apenas como argumento de palavra-chave). Será um argumento necessário no Django 2.0.

um exemplo de caso de uso é

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'Manufacturer',
        on_delete=models.CASCADE,
    )
    # ...

class Manufacturer(models.Model):
    # ...
    pass

O que on_delete faz? ( Eu acho que as ações a serem executadas se o modelo for excluído )

O que models.CASCADEfaz? ( alguma dica na documentação )

Que outras opções estão disponíveis ( se meu palpite estiver correto )?

Onde reside a documentação para isso?

Tudo é muito
fonte
Também há uma resposta para uma pergunta semelhante em stackoverflow.com/questions/47914325/… #
HelenM
11
O texto dessa pergunta semelhante agora está listado, abaixo, nesta resposta. Começa "FYI, o parâmetro on_delete nos modelos está ao contrário do que parece." Ele fornece muito mais detalhes do que as respostas originais.
precisa saber é o seguinte

Respostas:

778

Esse é o comportamento a ser adotado quando o objeto referenciado é excluído. Não é específico para o django, este é um padrão SQL.

Existem 6 ações possíveis a serem executadas quando esse evento ocorrer:

  • CASCADE: Quando o objeto referenciado for excluído, exclua também os objetos que têm referências a ele (quando você remove uma postagem de blog, por exemplo, também pode excluir comentários). SQL equivalentes: CASCADE.
  • PROTECT: Proíbe a exclusão do objeto referenciado. Para excluí-lo, você terá que excluir todos os objetos que fazem referência manualmente. SQL equivalentes: RESTRICT.
  • SET_NULL: Defina a referência como NULL (requer que o campo seja anulável). Por exemplo, ao excluir um usuário, você pode manter os comentários que ele postou nas postagens do blog, mas dizer que foram postados por um usuário anônimo (ou excluído). SQL equivalentes: SET NULL.
  • SET_DEFAULT: Defina o valor padrão. SQL equivalentes: SET DEFAULT.
  • SET(...): Defina um determinado valor. Este não faz parte do padrão SQL e é inteiramente tratado pelo Django.
  • DO_NOTHING: Provavelmente, é uma péssima idéia, pois isso criaria problemas de integridade no banco de dados (referenciando um objeto que realmente não existe). SQL equivalentes: NO ACTION.

Fonte: documentação do Django

Veja também a documentação do PostGreSQL, por exemplo.

Na maioria dos casos, CASCADEé o comportamento esperado, mas para cada ForeignKey, você deve sempre se perguntar qual é o comportamento esperado nessa situação. PROTECTe SET_NULLsão frequentemente úteis. Definir CASCADEonde não deveria, pode potencialmente excluir todo o seu banco de dados em cascata, simplesmente excluindo um único usuário.


Nota adicional para esclarecer a direção da cascata

É engraçado notar que a direção da CASCADEação não é clara para muitas pessoas. Na verdade, é engraçado notar que apenas a CASCADEação não é clara. Entendo que o comportamento em cascata pode ser confuso, no entanto, você deve pensar que é a mesma direção que qualquer outra ação . Assim, se você sente que a CASCADEdireção não está clara para você, na verdade significa que o on_deletecomportamento não está claro para você.

No seu banco de dados, uma chave estrangeira é basicamente representada por um campo inteiro cujo valor é a chave primária do objeto externo. Digamos que você tenha uma entrada comment_A , que possui uma chave estrangeira para uma entrada article_B . Se você excluir a entrada comment_A , tudo está bem, o article_B costumava viver sem comment_A e não se incomodava em ser excluído. No entanto, se você excluir article_B , comment_A entra em pânico! Ele nunca viveu sem article_B e precisa, faz parte de seus atributos ( article=article_B, mas o que é * article_B ** ???). É aqui que as on_deleteetapas para determinar como resolver esse erro de integridade, dizendo:

  • "Não! Por favor! Não! Não posso viver sem você!" (que é dito PROTECTna linguagem SQL)
  • "Tudo bem, se eu não sou seu, então não sou de ninguém" (o que é dito SET_NULL)
  • "Adeus mundo, eu não posso viver sem article_B" e cometer suicídio (esse é o CASCADEcomportamento).
  • "Tudo bem, eu tenho um amante de sobra, vou fazer referência ao article_C a partir de agora" ( SET_DEFAULT, ou até SET(...)).
  • "Eu não posso encarar a realidade, continuarei chamando seu nome, mesmo que essa seja a única coisa que me resta!" ( DO_NOTHING)

Espero que isso torne a direção da cascata mais clara. :)

Antoine Pinsard
fonte
19
Uma pergunta boba, mas cascata sempre deve ser uma direcional, certo? Ou seja, se Comment houver uma chave estrangeira para BlogPostexcluir o BlogPost, você deve excluir o comentário, mas a exclusão do comentário não deve excluir o BlogPost, independentemente do RDMS?
Anthony Manning-Franklin
20
@AnthonyManningFranklin Sure. A exclusão é acionada apenas quando uma referência é "interrompida". O que não é o caso quando você exclui um comentário, como você exclui a referência ao mesmo tempo.
Antoine Pinsard
6
A questão não é boba; Eu preciso dessa explicação também. Então, aqui assumimos que a relação é unilateral, o proprietário da relação é Commentquem tem o campo FK em sua tabela, enquanto BlogPost"possui" Comments se falamos sobre o modelo da vida real. Boa.
WesternGun 23/02
3
O importante é notar que definir um on_delete no Django NÃO cria uma cláusula ON DELETE no próprio banco de dados. O comportamento especificado (como CASCADE) afetará somente exclusões executadas via Django, e não exclusões brutas feitas diretamente no banco de dados.
JoeMjr2 5/03
2
Ótima explicação. Recebe um voto positivo de mim!
Homunculus Reticulli 19/03
42

O on_deletemétodo é usado para dizer ao Django o que fazer com instâncias de modelo que dependem da instância de modelo que você excluiu. (por exemplo, um ForeignKeyrelacionamento). O on_delete=models.CASCADEcomando diz ao Django para colocar em cascata o efeito de exclusão, ou seja, continue excluindo os modelos dependentes também.

Aqui está um exemplo mais concreto. Suponha que você tenha um Authormodelo que é um ForeignKeyem um Bookmodelo. Agora, se você excluir uma instância do Authormodelo, o Django não saberá o que fazer com instâncias do Bookmodelo que dependem dessa instância do Authormodelo. O on_deletemétodo diz ao Django o que fazer nesse caso. A configuração on_delete=models.CASCADEinstruirá o Django a aplicar em cascata o efeito de exclusão, ou seja, excluir todas as Bookinstâncias do modelo que dependem da Authorinstância que você excluiu.

Nota: on_deletese tornará um argumento obrigatório no Django 2.0. Nas versões mais antigas, o padrão é CASCADE.

Aqui está toda a documentação oficial.

him229
fonte
37

Para sua informação, o on_deleteparâmetro nos modelos é inverso ao que parece. Você coloca on_deleteuma chave estrangeira (FK) em um modelo para dizer ao django o que fazer se a entrada FK que você está apontando no seu registro for excluída. O opções de nossa loja têm usado a maioria são PROTECT, CASCADEe SET_NULL. Aqui estão as regras básicas que eu descobri:

  1. Use PROTECTquando o seu FK estiver apontando para uma tabela de consulta que realmente não deveria estar mudando e que certamente não deve causar sua alteração. Se alguém tentar excluir uma entrada dessa tabela de pesquisa, PROTECTimpedirá que ela seja excluída se estiver vinculada a algum registro. Também evita que o django exclua seu registro apenas porque excluiu uma entrada em uma tabela de pesquisa. Esta última parte é crítica. Se alguém excluísse o gênero "Feminino" da minha tabela de Gênero, CERTAMENTE NÃO gostaria que isso excluísse instantaneamente toda e qualquer pessoa que eu tivesse na minha tabela Pessoa que tivesse esse gênero.
  2. Use CASCADEquando o seu FK estiver apontando para um registro "pai". Portanto, se uma pessoa puder ter muitas entradas PersonEthnicity (ele / ela pode ser índio americano, preto e branco) e essa pessoa for excluída, eu realmente gostaria que quaisquer entradas PersonEthnicity "filho" fossem excluídas. Eles são irrelevantes sem a Pessoa.
  3. Use SET_NULLquando você não quer que as pessoas para ser autorizado a excluir uma entrada em uma tabela look-up, mas você ainda quer preservar seu registro. Por exemplo, se uma pessoa pode ter uma escola secundária, mas realmente não importa para mim se essa escola desaparecer na minha mesa de pesquisa, eu diria on_delete=SET_NULL. Isso deixaria meu registro pessoal lá fora; apenas definiria o FK do ensino médio na minha Pessoa como nulo. Obviamente, você terá que permitir null=Trueesse FK.

Aqui está um exemplo de um modelo que faz todas as três coisas:

class PurchPurchaseAccount(models.Model):
    id = models.AutoField(primary_key=True)
    purchase = models.ForeignKey(PurchPurchase, null=True, db_column='purchase', blank=True, on_delete=models.CASCADE) # If "parent" rec gone, delete "child" rec!!!
    paid_from_acct = models.ForeignKey(PurchPaidFromAcct, null=True, db_column='paid_from_acct', blank=True, on_delete=models.PROTECT) # Disallow lookup deletion & do not delete this rec.
    _updated = models.DateTimeField()
    _updatedby = models.ForeignKey(Person, null=True, db_column='_updatedby', blank=True, related_name='acctupdated_by', on_delete=models.SET_NULL) # Person records shouldn't be deleted, but if they are, preserve this PurchPurchaseAccount entry, and just set this person to null.

    def __unicode__(self):
        return str(self.paid_from_acct.display)
    class Meta:
        db_table = u'purch_purchase_account'

Como último petisco, você sabia que, se não especificar on_delete(ou não), o comportamento padrão é CASCADE? Isso significa que se alguém excluiu uma entrada de gênero na sua tabela Gênero, qualquer registro de Pessoa com esse gênero também foi excluído!

Eu diria: "Em caso de dúvida, defina on_delete=models.PROTECT". Em seguida, teste sua aplicação. Você descobrirá rapidamente quais FKs devem ser rotulados como outros valores sem pôr em risco nenhum dos seus dados.

Além disso, vale ressaltar que, on_delete=CASCADEna verdade, não foi adicionado a nenhuma das suas migrações, se esse for o comportamento que você está selecionando. Eu acho que é porque é o padrão, então colocar on_delete=CASCADEé a mesma coisa que colocar nada.

HelenM
fonte
12

Como mencionado anteriormente, o CASCADE excluirá o registro que possui uma chave estrangeira e fará referência a outro objeto que foi excluído. Por exemplo, se você tem um site imobiliário e uma propriedade que faz referência a uma cidade

class City(models.Model):
    # define model fields for a city

class Property(models.Model):
    city = models.ForeignKey(City, on_delete = models.CASCADE)
    # define model fields for a property

e agora, quando a cidade for excluída do banco de dados, todas as propriedades associadas (por exemplo, imóveis localizados nessa cidade) também serão excluídas do banco de dados

Agora também quero mencionar o mérito de outras opções, como SET_NULL ou SET_DEFAULT ou até DO_NOTHING. Basicamente, da perspectiva da administração, você deseja "excluir" esses registros. Mas você realmente não quer que eles desapareçam. Por muitas razões. Alguém pode ter excluído o arquivo acidentalmente ou para auditoria e monitoramento. E relatórios simples. Portanto, pode ser uma maneira de "desconectar" a propriedade de uma cidade. Novamente, isso dependerá de como o seu aplicativo for gravado.

Por exemplo, alguns aplicativos têm um campo "excluído" que é 0 ou 1. E todas as suas pesquisas e exibições de lista, etc., qualquer coisa que possa aparecer nos relatórios ou em qualquer lugar em que o usuário possa acessá-lo pelo front end, exclui qualquer coisa que esteja deleted == 1. No entanto, se você criar um relatório personalizado ou uma consulta personalizada para exibir uma lista de registros que foram excluídos e ainda mais para ver quando foi modificado pela última vez (outro campo) e por quem (por exemplo, quem o excluiu e quando). isso é muito vantajoso do ponto de vista executivo.

E não esqueça que você pode reverter exclusões acidentais tão simples quanto deleted = 0para esses registros.

Meu argumento é que, se houver uma funcionalidade, sempre haverá uma razão por trás disso. Nem sempre é uma boa razão. Mas uma razão. E muitas vezes uma boa também.

George Mogilevsky
fonte
3
Isso foi útil porque esclareceu em que direção o CASCADE ocorre. A resposta aceita não é clara se você não estiver familiarizado com as cascatas do SQL.
codescribblr
Obrigado, muito apreciado!
George Mogilevsky
2
Eu upvote esta resposta porque responde a minha dúvida sobre a direção do modelo de relacionamento
edepe
6

Aqui está a resposta para sua pergunta que diz: por que usamos on_delete?

Quando um objeto referenciado por uma ForeignKey é excluído, o Django, por padrão, emula o comportamento da restrição SQL ON DELETE CASCADE e também exclui o objeto que contém a ForeignKey. Esse comportamento pode ser substituído, especificando o argumento on_delete. Por exemplo, se você tiver uma ForeignKey anulável e desejar que ela seja definida como nula quando o objeto referenciado for excluído:

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)

Os valores possíveis para on_delete são encontrados em django.db.models:

CASCADE: exclusões em cascata; o padrão.

PROTECT: Impeça a exclusão do objeto referenciado aumentando ProtectedError, uma subclasse de django.db.IntegrityError.

SET_NULL: define o ForeignKey nulo; isso só é possível se nulo for True.

SET_DEFAULT: Defina a ForeignKey como seu valor padrão; um padrão para a ForeignKey deve ser definido.

Sonia Rani
fonte
Palavras simples deixam claro para mim, já que não sou maduro com sql e django. Obrigado.
wm.p1us 8/06
2

Digamos que você tenha dois modelos, um chamado Pessoa e outro chamado Empresas .

Por definição, uma pessoa pode criar mais de uma empresa.

Considerando que uma empresa pode ter uma e apenas uma pessoa, queremos que, quando uma pessoa for excluída, todas as empresas associadas a ela também sejam excluídas.

Então, começamos criando um modelo Person, como este

class Person(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.id+self.name

Então, o modelo de empresas pode ficar assim

class Companies(models.Model):
    title = models.CharField(max_length=20)
    description=models.CharField(max_length=10)
    person= models.ForeignKey(Person,related_name='persons',on_delete=models.CASCADE)

Observe o uso das on_delete=models.CASCADEempresas modelo. Isso é para excluir todas as empresas quando a pessoa que possui (instância da classe Person) é excluída.

Tiago Martins Peres
fonte
1

Reoriente seu modelo mental da funcionalidade de "CASCADE" pensando em adicionar um FK a uma cascata já existente (ou seja, uma cascata). A fonte dessa cascata é uma chave primária. Exclui o fluxo para baixo.

Portanto, se você definir o on_delete de um FK como "CASCADE", estará adicionando o registro desse FK a uma cascata de exclusões originárias do PK. O registro do FK pode participar dessa cascata ou não ("SET_NULL"). De fato, um registro com um FK pode até impedir o fluxo das exclusões! Construa uma barragem com "PROTEGER".

Gregory
fonte
0

Usar CASCADE significa dizer ao Django para excluir o registro referenciado. No exemplo do aplicativo de pesquisa abaixo: Quando uma 'Pergunta' é excluída, ela também exclui as Opções que esta Pergunta possui.

eg Pergunta: Como você ficou sabendo sobre nós? (Opções: 1. Amigos 2. Anúncio na TV 3. Mecanismo de pesquisa 4. Promoção por e-mail)

Quando você exclui esta pergunta, ela também exclui todas essas quatro opções da tabela. Observe a direção em que ele flui. Você não precisa colocar on_delete = models.CASCADE no modelo de perguntas, colocá-lo na opção.

from django.db import models



class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.dateTimeField('date_published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_legth=200)
    votes = models.IntegerField(default=0)
Kunal
fonte