django의 쿼리 세트에서 첫 번째 객체를 얻는 가장 빠른 방법은 무엇입니까?


193

종종 장고의 쿼리 세트에서 첫 번째 객체를 가져 오거나 반환하려고합니다. None 없는 경우 합니다. 이 작업을 수행하는 모든 방법이 많이 있습니다. 그러나 어느 것이 가장 성능이 좋은지 궁금합니다.

qs = MyModel.objects.filter(blah = blah)
if qs.count() > 0:
    return qs[0]
else:
    return None

이로 인해 두 번의 데이터베이스 호출이 발생합니까? 낭비 인 것 같습니다. 이게 더 빠릅니까?

qs = MyModel.objects.filter(blah = blah)
if len(qs) > 0:
    return qs[0]
else:
    return None

다른 옵션은 다음과 같습니다.

qs = MyModel.objects.filter(blah = blah)
try:
    return qs[0]
except IndexError:
    return None

이렇게하면 단일 데이터베이스 호출이 생성됩니다. 그러나 예외 객체를 생성하는 데는 많은 시간이 필요합니다. 이는 실제로 필요한 모든 것이 사소한 if-test 일 때 메모리를 많이 사용하는 작업입니다.

단 하나의 데이터베이스 호출로 예외 개체와 함께 메모리를 제거하지 않고 어떻게 할 수 있습니까?


21
경험 법칙 : DB 왕복을 최소화하는 것이 걱정된다면 len()쿼리 셋 에는 사용하지 말고 항상을 사용하십시오 .count().
Daniel DiPaolo

7
"예외 객체를 많이 생성하는 것은 메모리를 많이 사용하는 일입니다."-하나의 추가 예외를 만드는 것이 염려된다면, 파이썬이 예외를 사용하여 예외를 처리하는 것입니다. 실제로 귀하의 경우에 메모리 집약적이라는 벤치 마크를 했습니까?
lqc

1
@Leopd 그리고 만약 당신이 실제로 어떤 식 으로든 (또는 적어도 코멘트) 답변을 벤치마킹했다면, 그것이 더 빠르지 않다는 것을 알 것입니다. 실제로 추가 목록을 작성하면 그냥 버리기 때문에 속도가 느려질 수 있습니다. 그리고 파이썬 함수를 호출하거나 Django의 ORM을 처음 사용하는 비용과 비교할 때 땅콩입니다. filter ()에 대한 단일 호출은 여러 번 , 여러 번, 느리고 예외를 발생시킵니다 (반복 프로토콜이 작동하는 방식이기 때문에 여전히 제기 될 것입니다!).
lqc

1
성능 차이가 적다는 직관은 정확하지만 결론은 틀립니다. 나는 벤치 마크를 실행했으며 실제로 받아 들인 대답은 실제로 훨씬 빠릅니다. 그림을 이동.
Leopd

11
장고 1.6을 사용하는 사람들을 위해, 그들은 마지막으로 추가 한 first()last()편리한 방법 : docs.djangoproject.com/en/dev/ref/models/querysets/#first
웨이 엔

답변:


328

(2013 11월 해제) 장고 1.6 도입 편의 방법 first()last()어떤 생성 된 예외 복귀 삼키는 None의 검색어가 더 객체를 반환하지 않는 경우.


2
[: 1]을 수행하지 않으므로 빠르지 않습니다 (어쨌든 전체 쿼리 세트를 평가할 필요가 없다면).
janek37

13
또한, first()last()시행 ORDER BY쿼리에 절을. 결과가 결정적이지만 쿼리 속도가 느려질 것입니다.
Phil Krylov 2016 년

@ janek37 성능에는 차이가 없습니다. cod3monk3y로 표시된 바와 같이 편리한 방법이며 전체 쿼리 세트를 읽지 않습니다.
Zompa

143

정답은

Entry.objects.all()[:1].get()

사용할 수있는 것 :

Entry.objects.filter()[:1].get()

먼저 모든 레코드의 전체 데이터베이스 호출을 강제로 수행하므로 목록으로 전환하지 않으려 고합니다. 위의 작업을 수행하면 첫 번째 만 가져옵니다. 당신은 심지어 사용할 수 있습니다.order_by 당신이 원하는 첫 번째를 얻을 수 있도록 수 있습니다.

.get()객체 를 추가 하지 않고 QuerySet을 다시 가져 오십시오.


9
원래 세 번째 옵션과 비슷하지만 슬라이싱이있는 ObjectDoesNotExist를 제외하고는 시도해보십시오.
Danny W. Adair

1
결국 get ()을 호출 할 경우 LIMIT를 설정하는 요점은 무엇입니까? ORM과 SQL 컴파일러가 백엔드에 가장 적합한 것을 결정하게하십시오 (예를 들어, Oracle Django는 LIMIT를 에뮬레이트하므로 도움이 아니라 아프게됩니다).
lqc

후행 .get () 없이이 답변을 사용했습니다. 목록이 반환되면 목록의 첫 번째 요소를 반환합니다.
키스 존 허치슨

다른 점은 무엇 Entry.objects.all()[0]입니까 ??
James Lin

15
@JamesLin 차이점은 [: 1] .get ()은 DoesNotExist를 발생시키고 [0]은 IndexError를 발생시킵니다.
Ropez

49
r = list(qs[:1])
if r:
  return r[0]
return None

1
추적 기능을 켜면 LIMIT 1쿼리 에이 추가 내용이 표시 될 것 입니다. 이보다 더 나은 작업을 수행 할 수 있는지 잘 모르겠습니다. 그러나 내부적 __nonzero__으로 QuerySettry: iter(self).next() except StopIteration: return false...예외를 벗어나지 않도록 구현됩니다 .
벤 잭슨

@Ben : QuerySet.__nonzero__()은 true 를 확인하기 전에 QuerySet로 변환 되므로 호출되지 않습니다 list. 그러나 다른 예외가 여전히 발생할 수 있습니다.
Ignacio Vazquez-Abrams

@Aron : StopIteration예외 가 발생할 수 있습니다 .
Ignacio Vazquez-Abrams

list === 호출 __iter__로 변환하여 새 반복자 객체를 가져오고 throw next될 때까지 메소드를 호출합니다 StopIteration. 따라서 확실히 어딘가에 예외가있을 것입니다;)
lqc

14
이 답변은 이제 구식입니다. Django 1.6+에 대한 @ cod3monk3y 답변을 살펴보십시오.
ValAyal

37

이제 Django 1.9에는 first() 쿼리 세트에 대한 메소드가 있습니다.

YourModel.objects.all().first()

이것은 queryset이 ​​비어 있으면 예외를 throw하지 않는 것보다 낫 .get()거나 더 나은 방법 [0]입니다. 따라서, 당신은 사용을 확인할 필요가 없습니다.exists()


1
이로 인해 SQL에서 LIMIT 1이 발생하고 쿼리 속도가 느려질 수 있다고 주장했습니다. 따라서 위의 대답은 괜찮다고 생각하지만 증거가 확인되는 것을보고 싶습니다.
rrauenza 2016 년

나는 "더 나은"이라고 말하지 않을 것입니다. 그것은 실제로 당신의 기대에 달려 있습니다.
trigras

7

첫 번째 요소를 자주 얻으려면 다음과 같이 QuerySet을 확장하십시오.

class FirstQuerySet(models.query.QuerySet):
    def first(self):
        return self[0]


class ManagerWithFirstQuery(models.Manager):
    def get_query_set(self):
        return FirstQuerySet(self.model)

다음과 같이 모델을 정의하십시오.

class MyModel(models.Model):
    objects = ManagerWithFirstQuery()

그리고 이것을 다음과 같이 사용하십시오 :

 first_object = MyModel.objects.filter(x=100).first()

전화는 = ManagerWithFirstQuery 객체 = ManagerWithFirstQuery ()와 같은 객체 - DONT 괄호를 잊지 - 어쨌든, 당신이 나에게 그렇게 1 도움
카밀

7

이것은 잘 작동 할 수 있습니다 :

def get_first_element(MyModel):
    my_query = MyModel.objects.all()
    return my_query[:1]

비어 있으면 빈 목록을 반환하고, 그렇지 않으면 목록 내의 첫 번째 요소를 반환합니다.


1
이것은 지금까지 가장 좋은 솔루션입니다 ... 데이터베이스를 한 번만 호출하면됩니다
Shh

5

이렇게 될 수 있습니다

obj = model.objects.filter(id=emp_id)[0]

또는

obj = model.objects.latest('id')

3

존재와 같은 django 메소드를 사용해야합니다. 당신이 그것을 사용할 수 있습니다.

if qs.exists():
    return qs[0]
return None

1
내가 올바르게 이해한다면, 관용적 파이썬은 일반적으로 도약 전에 접근 방식보다는 EAFP ( 권한보다 용서 요청 ) 접근 방식을 사용합니다.
BigSmoke

EAFP는 단순한 스타일 권장 사항이 아니라 이유가 있습니다 (예 : 파일을 열기 전에 확인해도 오류가 발생하지 않음). 여기서 관련 고려 사항은 존재 + 항목 가져 오기가 두 개의 데이터베이스 쿼리를 유발한다는 것입니다. 프로젝트 및보기에 따라 바람직하지 않을 수 있습니다.
Éric Araujo

2

django 1.6부터 first () 메소드와 함께 filter ()를 다음과 같이 사용할 수 있습니다.

Model.objects.filter(field_name=some_param).first()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.