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__으로 QuerySet는 try: 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.