Eu estava discutindo isso com alguns colegas. Existe uma maneira preferida de recuperar um objeto no Django quando você espera apenas um?
As duas maneiras óbvias são:
try:
obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
# We have no object! Do something...
pass
E:
objs = MyModel.objects.filter(id=1)
if len(objs) == 1:
obj = objs[0]
else:
# We have no object! Do something...
pass
O primeiro método parece comportamentalmente mais correto, mas usa exceções no fluxo de controle que podem introduzir alguma sobrecarga. A segunda é mais indireta, mas nunca gera uma exceção.
Quaisquer pensamentos sobre qual deles é preferível? Qual é mais eficiente?
QS.get()
é bom. 2. Detalhes importam: "esperar apenas um" significa sempre 0-1 objetos, ou é possível ter mais de 2 objetos e esse caso também deve ser tratado (neste caso,len(objs)
é uma péssima idéia)? 3. Não assuma nada sobre sobrecarga sem uma referência (acho que, nesse casotry/except
, será mais rápido enquanto pelo menos metade das chamadas retornar algo))Você pode instalar um módulo chamado django-annoying e faça o seguinte:
fonte
1 está correto. No Python, uma exceção tem sobrecarga igual a um retorno. Para uma prova simplificada, você pode ver isso .
2 É isso que o Django está fazendo no back-end.
get
chamafilter
e gera uma exceção se nenhum item for encontrado ou se mais de um objeto for encontrado.fonte
try
.Estou um pouco atrasado para a festa, mas com o Django 1.6 existe o
first()
método nos conjuntos de consultas.https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first
Exemplo:
fonte
Não posso falar com nenhuma experiência do Django, mas a opção nº 1 diz claramente ao sistema que você está solicitando um objeto, enquanto a segunda opção não. Isso significa que a opção 1 pode aproveitar mais facilmente os índices de cache ou banco de dados, especialmente onde o atributo no qual você está filtrando não é garantido como único.
Além disso (novamente, especulando), a segunda opção pode ter que criar algum tipo de coleção de resultados ou objeto de iterador, pois a chamada filter () normalmente poderia retornar muitas linhas. Você ignoraria isso com get ().
Por fim, a primeira opção é mais curta e omite a variável temporária extra - apenas uma pequena diferença, mas cada pequena ajuda.
fonte
Por que todo esse trabalho? Substitua 4 linhas por 1 atalho embutido. (Isso faz sua própria tentativa / exceto).
fonte
Model.objects.get_or_create()
é paraMais algumas informações sobre exceções. Se eles não são criados, eles custam quase nada. Portanto, se você sabe que provavelmente terá um resultado, use a exceção, pois ao usar uma expressão condicional, você paga o custo de verificar todas as vezes, não importa o quê. Por outro lado, eles custam um pouco mais do que uma expressão condicional quando são aumentados; portanto, se você espera não ter um resultado com alguma frequência (digamos, 30% do tempo, se a memória servir), a verificação condicional acaba ser um pouco mais barato.
Mas este é o ORM do Django, e provavelmente a ida e volta ao banco de dados, ou mesmo um resultado em cache, provavelmente domina as características de desempenho, portanto favorece a legibilidade, neste caso, já que você espera exatamente um resultado, use
get()
.fonte
Eu brinquei um pouco com esse problema e descobri que a opção 2 executa duas consultas SQL, o que para uma tarefa tão simples é excessivo. Veja minha anotação:
Uma versão equivalente que executa uma única consulta é:
Ao mudar para essa abordagem, consegui reduzir substancialmente o número de consultas que meu aplicativo executa.
fonte
Pergunta interessante, mas para mim a opção 2 cheira a otimização prematura. Não sei ao certo qual é o melhor desempenho, mas a opção 1 certamente parece e parece mais pitônica para mim.
fonte
Eu sugiro um design diferente.
Se você deseja executar uma função em um resultado possível, pode derivar do QuerySet, desta forma: http://djangosnippets.org/snippets/734/
O resultado é impressionante, você pode, por exemplo:
Aqui, o filtro retorna um conjunto de consultas vazio ou um conjunto de consultas com um único item. Suas funções personalizadas do conjunto de consultas também são encadeadas e reutilizáveis. Se você quiser que o faça por todas as suas entradas:
MyModel.objects.all().yourFunction()
.Eles também são ideais para serem usados como ações na interface administrativa:
fonte
A opção 1 é mais elegante, mas use try..except.
Pela minha própria experiência, posso lhe dizer que, às vezes, você tem certeza de que não pode haver mais de um objeto correspondente no banco de dados, e ainda haverá dois ... (exceto, é claro, ao obter o objeto por sua chave primária).
fonte
Lamento adicionar mais uma opinião sobre esse problema, mas estou usando o paginador django e, no meu aplicativo de administração de dados, o usuário pode escolher o que consultar. Às vezes, esse é o ID de um documento, mas, caso contrário, é uma consulta geral retornando mais de um objeto, ou seja, um conjunto de consultas.
Se o usuário consultar o ID, eu posso executar:
que gera um erro no paginador do django, porque é um registro e não um conjunto de registros de consultas.
Eu preciso executar:
O que retorna um Queryset com um item. Então o paginador funciona muito bem.
fonte
.obter()
.filtro()
Nota
fonte