No Django doc,
select_related()
"segue" relacionamentos de chave estrangeira, selecionando dados adicionais de objetos relacionados ao executar sua consulta.
prefetch_related()
faz uma pesquisa separada para cada relacionamento e faz a "junção" no Python.
O que significa "fazer a junção em python"? Alguém pode ilustrar com um exemplo?
Meu entendimento é que, para relacionamento com chave estrangeira, use select_related
; e para relacionamento M2M, use prefetch_related
. Isso está correto?
python
django
django-models
django-orm
NeoWang
fonte
fonte
Respostas:
Sua compreensão está correta. Você usa
select_related
quando o objeto que você selecionará é um único objeto,OneToOneField
um ou maisForeignKey
. Você usaprefetch_related
quando deseja obter um "conjunto" de coisas,ManyToManyField
como você afirmou ou inverteForeignKey
. Apenas para esclarecer o que quero dizer com "reverseForeignKey
s", aqui está um exemplo:A diferença é que
select_related
uma junção SQL e, portanto, recupera os resultados como parte da tabela do servidor SQL.prefetch_related
por outro lado, executa outra consulta e, portanto, reduz as colunas redundantes no objeto original (ModelA
no exemplo acima). Você pode usarprefetch_related
para qualquer coisa que possa usarselect_related
.As desvantagens são que
prefetch_related
é necessário criar e enviar uma lista de IDs para selecionar de volta ao servidor, isso pode levar um tempo. Não tenho certeza se existe uma boa maneira de fazer isso em uma transação, mas meu entendimento é que o Django sempre envia uma lista e diz SELECT ... WHERE pk IN (..., ..., ...) basicamente. Nesse caso, se os dados pré-buscados forem escassos (digamos, objetos do Estado dos EUA vinculados aos endereços das pessoas), isso pode ser muito bom; no entanto, se estiver mais próximo de um para um, isso pode desperdiçar muitas comunicações. Em caso de dúvida, tente os dois e veja qual funciona melhor.Tudo discutido acima é basicamente sobre as comunicações com o banco de dados. No lado do Python, no entanto,
prefetch_related
há o benefício extra de um único objeto ser usado para representar cada objeto no banco de dados. Comselect_related
objetos duplicados serão criados em Python para cada objeto "pai". Como os objetos em Python têm um pouco de sobrecarga de memória, isso também pode ser uma consideração.fonte
select_related
é uma consulta enquantoprefetch_related
são duas; portanto, a primeira é mais rápida. Masselect_related
não vai ajudá-lo paraManyToManyField
'sselect_related
usa um JOIN no SQL, enquantoprefetch_related
executa a consulta no primeiro modelo, coleta todos os IDs necessários para pré-buscar e, em seguida, executa uma consulta com uma cláusula IN no WHERE com todos os IDs necessários. Se você diz 3-5 modelos usando a mesma chave estrangeira,select_related
certamente será melhor. Se você possui centenas ou milhares de modelos usando a mesma chave estrangeira,prefetch_related
pode ser realmente melhor. Entre você terá que testar e ver o que acontece.Ambos os métodos atingem o mesmo objetivo, para renunciar a consultas de banco de dados desnecessárias. Mas eles usam abordagens diferentes para eficiência.
O único motivo para usar um desses métodos é quando uma única consulta grande é preferível a muitas consultas pequenas. O Django usa a consulta grande para criar modelos na memória de preferência, em vez de executar consultas sob demanda no banco de dados.
select_related
realiza uma junção com cada pesquisa, mas estende a seleção para incluir as colunas de todas as tabelas unidas. No entanto, essa abordagem tem uma ressalva.As junções têm o potencial de multiplicar o número de linhas em uma consulta. Quando você executa uma junção em uma chave estrangeira ou em campo individual, o número de linhas não aumenta. No entanto, junções muitos para muitos não têm essa garantia. Então, o Django restringe
select_related
relações que não resultarão inesperadamente em uma junção massiva.O "join in python" para
prefetch_related
é um pouco mais alarmante do que deveria ser. Ele cria uma consulta separada para cada tabela a ser unida. Ele filtra cada uma dessas tabelas com uma cláusula WHERE IN, como:Em vez de executar uma única junção com potencialmente muitas linhas, cada tabela é dividida em uma consulta separada.
fonte
Como a documentação do Django diz:
Mais informações sobre isso: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#prefetch-related
fonte
Passou pelas respostas já postadas. Apenas pensei que seria melhor se eu adicionar uma resposta com exemplo real.
Digamos que você tenha 3 modelos de Django relacionados.
Aqui você pode consultar o
M2
modelo e seusM1
objetos relativos usandoselect_relation
campo eM3
objetos usandoprefetch_relation
campo.No entanto, como mencionamos
M1
a relação deM2
é aForeignKey
, ela retorna apenas 1 registro para qualquerM2
objeto. O mesmo se aplicaOneToOneField
também.Mas
M3
a relação deM2
é umaManyToManyField
que pode retornar qualquer número deM1
objetos.Considere um caso em que você tem 2
M2
objetosm21
,m22
que possuem os mesmos 5M3
objetos associados aos IDs1,2,3,4,5
. Quando você buscaM3
objetos associados para cada um dessesM2
objetos, se você usa o select related, é assim que funciona.Passos:
m21
objeto.M3
objetos relacionados aom21
objeto cujos IDs são1,2,3,4,5
.m22
objeto e todos os outrosM2
objetos.Como temos os mesmos
1,2,3,4,5
IDs para ambosm21
,m22
objetos, se usarmos a opção select_related, ele consultará o banco de dados duas vezes pelos mesmos IDs que já foram buscados.Em vez disso, se você usar prefetch_related, ao tentar obter
M2
objetos, ele anotará todos os IDs que seus objetos retornaram (Nota: apenas os IDs) enquanto consultava aM2
tabela e, como último passo, o Django fará uma consulta àM3
tabela com o conjunto de todos os IDs que seusM2
objetos retornaram. e junte-os aM2
objetos usando Python em vez de banco de dados.Dessa forma, você está consultando todos os
M3
objetos apenas uma vez, o que melhora o desempenho.fonte