Pergunta

Eu estava tendo um debate sobre isso com alguns colegas. Existe uma maneira preferida para recuperar um objeto em Django quando você está esperando 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. O segundo é mais rotunda, mas nunca vai gerar uma exceção.

Todos os pensamentos sobre qual destes é preferível? Que é mais eficiente?

Foi útil?

Solução

get() é fornecido especificamente para este caso. Usá-lo.

A opção 2 é quase exatamente como o método get() é realmente implementado em Django, então não deve haver nenhuma diferença "performance" (eo fato de que você está pensando sobre isso indica que você está violando uma das regras principais da programação , ou seja, tentando código otimizar antes mesmo foi escrito e perfilados - até que você tenha o código e pode executá-lo, você não sabe como ele irá executar, e tentando otimizar antes, então é um caminho de dor)

Outras dicas

Você pode instalar um módulo chamado django-irritante e, em seguida, faça o seguinte:

from annoying.functions import get_object_or_None

obj = get_object_or_None(MyModel, id=1)

if not obj:
    #omg the object was not found do some error stuff

1 está correta. Em Python uma excepção tem igual sobrecarga para um retorno. Para uma prova simplificado você pode olhar para este .

2 Isto é o que Django está fazendo no backend. get chama filter e levanta uma exceção se nenhum item for encontrado ou se mais de um objeto é encontrado.

Estou um pouco atrasado para a festa, mas com Django 1.6 não é o método first() em querysets.

https: / /docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first


Retorna o primeiro objeto acompanhado pelo queryset ou Nenhum se não houver nenhum objeto correspondente. Se o QuerySet não tem nenhuma ordenação definida, então o queryset é automaticamente ordenadas pela chave primária.

Exemplo:

p = Article.objects.order_by('title', 'pub_date').first()
Note that first() is a convenience method, the following code sample is equivalent to the above example:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None

Eu não posso falar com alguma experiência de Django, mas a opção # 1 diz claramente o sistema que você está pedindo um objeto, enquanto a segunda opção não. Isto significa que a opção # 1 poderia mais facilmente tirar proveito de cache do banco de dados ou índices, especialmente onde o atributo que você está filtrando não é garantido para ser único.

Além disso (de novo, especulando) a segunda opção pode ter que criar algum tipo de coleta de resultados ou iterador objeto desde a chamada filter () normalmente poderia retornar várias linhas. Você teria de bypass isso com get ().

Finalmente, a primeira opção é tanto mais curto e omite a variável temporária adicional -. Apenas uma pequena diferença, mas cada pequena ajuda

Por que fazer todo esse trabalho? Substituir 4 linhas com um atalho incorporado. (Isto faz sua própria try / excepto).

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)

Alguns mais informações sobre exceções. Se eles não são ressuscitados, eles custam quase nada. Assim, se você sabe que você provavelmente vai ter um resultado, use a exceção, uma vez usando uma expressão condicional que você paga o custo da verificação de cada vez, não importa o quê. Por outro lado, eles custam um pouco mais do que uma expressão condicional quando eles são levantados, por isso, se você espera não ter um resultado com alguma frequência (digamos, 30% do tempo, se a memória), a verificação condicional despeja a ser um pouco mais barato.

Mas este é ORM do Django, e provavelmente o de ida e volta ao banco de dados, ou mesmo um resultado em cache, é provável que dominam as características de desempenho, de modo a legibilidade favor, neste caso, uma vez que se espera exatamente um resultado, get() uso .

Eu tenho jogado com este problema um pouco e descobriu que a opção 2 Executa duas consultas SQL, que para tal tarefa simples é excessivo. Ver a minha anotação:

objs = MyModel.objects.filter(id=1) # This does not execute any SQL
if len(objs) == 1: # This executes SELECT COUNT(*) FROM XXX WHERE filter
    obj = objs[0]  # This executes SELECT x, y, z, .. FROM XXX WHERE filter
else: 
    # we have no object!  do something
    pass

Uma versão equivalente que executa uma única consulta é:

items = [item for item in MyModel.objects.filter(id=1)] # executes SELECT x, y, z FROM XXX WHERE filter
count = len(items) # Does not execute any query, items is a standard list.
if count == 0:
   return None
return items[0]

Ao mudar para esta abordagem, eu era capaz de reduzir substancialmente o número de consultas meus executa aplicativos.

É uma pergunta interessante, mas por opção me # 2 cheira a otimização prematura. Eu não tenho certeza que é mais alto desempenho, mas a opção # 1 certamente parece e se sente mais Python para mim.

Eu sugiro um design diferente.

Se você quiser executar uma função em um resultado possível, você poderia derivar de QuerySet, como este: http: //djangosnippets.org/snippets/734/

O resultado é bastante impressionante, você poderia, por exemplo:

MyModel.objects.filter(id=1).yourFunction()

Aqui, filtro retorna quer um queryset vazio ou um queryset com um único item. Suas funções queryset personalizados também são chainable e reutilizável. 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 de administração:

def yourAction(self, request, queryset):
    queryset.yourFunction()

A opção 1 é mais elegante, mas certifique-se de uso try..except.

De minha própria experiência, posso dizer-lhe que às vezes você está certo lá não pode, eventualmente, ser mais do que um objeto correspondente no banco de dados, e ainda haverá dois ... (exceto, claro, quando chegar o objeto por seu principal key).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top