장고 필터 대 단일 객체 얻기?


147

나는 동료들과 이것에 대해 토론하고 있었다. 장고에서 객체를 하나만 기대할 때 선호하는 방법이 있습니까?

두 가지 확실한 방법은 다음과 같습니다.

try:
    obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
    # We have no object! Do something...
    pass

과:

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

if len(objs) == 1:
    obj = objs[0]
else:
    # We have no object! Do something...
    pass

첫 번째 방법은 동작이 더 정확 해 보이지만 제어 흐름에서 예외를 사용하여 오버 헤드가 발생할 수 있습니다. 두 번째는 더 우회하지만 예외는 발생하지 않습니다.

이 중 어느 쪽이 바람직한 지 생각하십니까? 어느 것이 더 효율적입니까?

답변:


177

get()이 경우에 특별히 제공됩니다 . 그걸 써.

옵션 2는 거의 get()장고에서 메소드가 실제로 구현 되는 방식과 거의 동일 하므로 "성능"차이가 없어야합니다 (생각하고 있다는 사실은 프로그래밍의 기본 규칙 중 하나를 위반한다는 것을 나타냅니다. 코드를 작성하고 프로파일 링하기 전에 코드를 최적화하십시오. 코드를 가지고 실행할 수있을 때까지 코드의 성능을 모르고 그 전에 최적화를 시도하는 것이 고통의 경로입니다).


모든 것이 정확하지만 답변을 위해 더 많은 정보를 추가해야합니까? 1. 파이썬은 try / except ( EAFP 참조 )를 권장 하므로 이것이 QS.get()좋습니다. 2. 세부 사항 : "하나만 기대하는 것"은 항상 0-1 개의 개체를 의미합니까, 아니면 2 개 이상의 개체를 가질 수 있으며이 경우도 처리해야합니다 (이 경우 len(objs)에는 끔찍한 아이디어 임)? 3. 벤치 마크없이 오버 헤드에 대해 아무 것도 가정하지 마십시오 (이 경우 try/except전화의 절반 이상이 무언가를 반환하는 한 더 빠를 것이라고 생각합니다 )
imposeren

> 코드를 작성하고 프로파일 링하기 전에 코드 최적화를 시도하는 것은 흥미로운 말입니다. 나는 항상 구현하기 전에 무언가를 구현하는 가장 선택적 방법을 생각해야한다고 생각했습니다. 그게 잘못이야? 이 점에 대해 자세히 설명해 주시겠습니까? 이것을 자세히 설명하는 자료가 있습니까?
Parth Sharma 12

아무도 먼저 언급하지 않은 것에 놀랐습니다. 다른 조언은이 시나리오에 대한 요청임을 나타냅니다. stackoverflow.com/questions/5123839/…
NeilG

29

django-annoying 이라는 모듈을 설치 한 후 다음을 수행 할 수 있습니다 .

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
왜 그런 방법을 갖는 것이 성가신가? 나에게 잘 보인다!
토마스

17

1이 맞습니다. 파이썬에서 예외는 리턴과 동일한 오버 헤드를 갖습니다. 간단한 증명을 위해 이것을 볼 수 있습니다 .

2 장고가 백엔드에서하는 일입니다. 항목이 없거나 둘 이상의 개체가있는 경우 예외를 get호출 filter하고 발생시킵니다.


1
그 시험은 꽤 불공평합니다. 예외를 발생시키는 오버 헤드의 대부분은 스택 추적 처리입니다. 이 테스트의 스택 길이는 1이며 일반적으로 응용 프로그램에서 찾을 수있는 것보다 훨씬 낮습니다.
Rob Young

@Rob Young : 무슨 뜻인가요? 일반적인 "허가보다는 요청"체계에서 스택 추적 처리를 어디에서 볼 수 있습니까? 처리 시간은 예외가 발생하는 거리에 따라 달라집니다. 우리가 java로 작성하지 않고 e.printStackTrace ()를 호출하는 경우에는 얼마나 깊이 발생하는지가 아닙니다. 그리고 가장 자주 (사전 조회와 같이) 예외는 바로 아래에 발생합니다 try.
Tomasz Gandor

12

나는 파티에 조금 늦었지만 장고 1.6에는 first()쿼리 세트에 대한 방법이 있습니다.

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


쿼리 세트와 일치하는 첫 번째 개체를 반환하거나 일치하는 개체가 없으면 None을 반환합니다. QuerySet에 순서가 정의되어 있지 않으면 기본 키에 의해 쿼리 세트가 자동으로 정렬됩니다.

예:

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

그것은 당신이 쿼리에 하나의 개체 만이 보장하지 않습니다
py_dude

8

Django의 경험으로 말할 수는 없지만 옵션 # 1은 시스템에 하나의 객체를 요구하고 있지만 두 번째 옵션은 그렇지 않다는 것을 분명히 알려줍니다. 이는 옵션 # 1이 캐시 또는 데이터베이스 인덱스를보다 쉽게 ​​활용할 수 있음을 의미합니다. 특히 필터링하는 속성이 고유하지 않은 경우에 특히 그렇습니다.

또한 두 번째 옵션은 filter () 호출이 일반적으로 많은 행을 리턴 할 수 있기 때문에 일종의 결과 콜렉션 또는 반복자 오브젝트를 작성해야 할 수도 있습니다. get ()으로 이것을 무시할 것입니다.

마지막으로 첫 번째 옵션은 짧고 여분의 임시 변수를 생략합니다. 약간의 차이 만 있지만 모든 작은 도움이됩니다.


장고에 대한 경험은 없지만 여전히 발견됩니다. 기본적으로 명시적이고 간결하며 안전하다는 것은 언어 나 프레임 워크에 관계없이 좋은 원칙입니다.
nevelis

8

왜 모든 것이 작동합니까? 4 줄을 하나의 내장 단축키로 바꿉니다. (이것은 자체 시도 / 제외를 수행합니다.)

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)

1
원하는 동작 일 때 유용하지만 때로는 누락 된 개체를 만들거나 풀이 선택적인 정보 일 수 있습니다.
SingleNegationElimination는 1

2
즉 무엇 Model.objects.get_or_create()을위한
boatcoder

7

예외에 대한 추가 정보. 그들이 자라지 않으면 거의 비용이 들지 않습니다. 따라서 결과가 나올 것이라는 것을 알고 있다면 예외를 사용하십시오. 조건식을 사용하면 무엇이든 상관없이 매번 검사 비용을 지불하기 때문입니다. 반면에, 그것들은 그들이 올라올 때 조건식보다 약간 더 비쌉니다. 따라서 어떤 빈도 (예 : 메모리가 제공되는 경우 30 %의 시간)로 결과를 기대하지 않으면 조건부 검사가 나타납니다 조금 더 저렴합니다.

그러나 이것은 장고의 ORM이며 아마도 데이터베이스로의 왕복 또는 캐시 된 결과가 성능 특성을 지배 할 가능성이 높 으므로이 경우 가독성을 선호하십시오 get(). 정확히 하나의 결과를 기대하기 때문에를 사용하십시오 .


4

나는이 문제를 조금 가지고 놀고 옵션 2가 두 개의 SQL 쿼리를 실행한다는 것을 발견했습니다.이 간단한 작업에는 과도합니다. 내 주석보기 :

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

단일 쿼리를 실행하는 동등한 버전은 다음과 같습니다.

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]

이 접근 방식으로 전환함으로써 응용 프로그램이 실행하는 쿼리 수를 크게 줄일 수있었습니다.


1

흥미로운 질문이지만, 옵션 2는 조기 최적화의 악취입니다. 어느 것이 더 성능이 좋은지 확실하지 않지만 옵션 # 1은 확실히 나에게 더 화려하게 보입니다.


1

다른 디자인을 제안합니다.

가능한 결과에 대해 기능을 수행하려는 경우 다음과 같이 QuerySet에서 파생 될 수 있습니다. http://djangosnippets.org/snippets/734/

결과는 매우 훌륭합니다. 예를 들면 다음과 같습니다.

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

여기서 필터는 빈 쿼리 집합 또는 단일 항목이있는 쿼리 집합을 반환합니다. 사용자 정의 queryset 함수도 체인 가능하고 재사용 가능합니다. 모든 항목에 대해 수행하려는 경우 : MyModel.objects.all().yourFunction().

또한 관리자 인터페이스에서 작업으로 사용하기에 이상적입니다.

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

0

옵션 1은 더 우아하지만 try..except를 사용해야합니다.

내 경험에 따르면 데이터베이스에 일치하는 객체가 둘 이상있을 수는 없지만 때로는 기본 키로 객체를 가져올 때를 제외하고는 두 개가있을 것이라고 확신 할 수 있습니다.


0

이 문제에 대해 하나 더 가져 가서 죄송하지만 django paginator를 사용하고 있으며 데이터 관리 앱에서 사용자가 무엇을 쿼리할지 선택할 수 있습니다. 때로는 문서의 ID이지만 둘 이상의 객체, 즉 Queryset을 반환하는 일반적인 쿼리입니다.

사용자가 ID를 쿼리하면 다음을 실행할 수 있습니다.

Record.objects.get(pk=id)

django의 paginator에 레코드가 아니라 레코드이기 때문에 오류가 발생합니다.

나는 실행해야합니다 :

Record.objects.filter(pk=id)

하나의 항목이 포함 된 Queryset을 반환합니다. 그러면 페이지 매김이 제대로 작동합니다.


페이지 매김 또는 QuerySet이 필요한 기능을 사용하려면 쿼리에서 QuerySet을 반환해야합니다. 이미 알고 있듯이 .filter ()와 .get () 사이를 전환하지 말고 .filter ()를 사용하고 "pk = id"필터를 제공하십시오. 이것이이 사용 사례의 패턴입니다.
Cornel Masson

0

.가져 오기()

지정된 조회 매개 변수와 일치하는 객체를 반환합니다.이 조회 매개 변수는 필드 조회에 설명 된 형식이어야합니다.

get ()은 여러 객체가 발견되면 MultipleObjectsReturned를 발생시킵니다. MultipleObjectsReturned 예외는 모델 클래스의 속성입니다.

지정된 매개 변수에 대해 개체를 찾지 못하면 get ()에서 DoesNotExist 예외를 발생시킵니다. 이 예외는 모델 클래스의 속성이기도합니다.

.필터()

주어진 조회 매개 변수와 일치하는 객체를 포함하는 새로운 QuerySet을 반환합니다.

노트

고유 한 단일 객체를 얻으려면 get ()을 사용하고 조회 매개 변수와 일치하는 모든 객체를 얻으려면 filter ()를 사용하십시오.

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