장고 필터 대 단일 객체 얻기?
나는 몇몇 동료들과 이것에 대해 토론하고 있었다. 장고에서 객체를 하나만 기대할 때 선호하는 방법이 있습니까?
두 가지 확실한 방법은 다음과 같습니다.
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
첫 번째 방법은 동작이 더 정확 해 보이지만 제어 흐름에서 예외를 사용하여 오버 헤드가 발생할 수 있습니다. 두 번째는 더 우회하지만 예외는 발생하지 않습니다.
이 중 어느 쪽이 바람직한 지 생각하십니까? 어느 것이 더 효율적입니까?
get()
이 경우에 특별히 제공됩니다. 그걸 써.
옵션 2는 거의 get()
장고에서 메소드가 실제로 구현 되는 방식과 거의 동일 하므로 "성능"차이가 없어야합니다 (생각하고 있다는 사실은 프로그래밍의 기본 규칙 중 하나를 위반한다는 것을 나타냅니다. 코드를 작성하고 프로파일 링하기 전에 코드를 최적화하십시오. 코드를 가지고 실행할 수있을 때까지 코드의 성능을 모르고 그 전에 최적화를 시도하는 것이 고통의 경로입니다).
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이 맞습니다. 파이썬에서 예외는 리턴과 동일한 오버 헤드를 갖습니다. 간단한 증명을 위해 이것을 볼 수 있습니다 .
2 장고가 백엔드에서하는 일입니다. 항목이 없거나 둘 이상의 개체가있는 경우 예외를 get
호출 filter
하고 발생시킵니다.
나는 파티에 조금 늦었지만 장고 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
Django의 경험으로 말할 수는 없지만 옵션 # 1은 시스템에 하나의 객체를 요청하고 있지만 두 번째 옵션은 그렇지 않다는 것을 명확하게 알려줍니다. 이는 옵션 # 1이 캐시 또는 데이터베이스 인덱스를보다 쉽게 활용할 수 있음을 의미합니다. 특히 필터링하는 속성이 고유하지 않은 경우에 그러합니다.
또한 두 번째 옵션은 filter () 호출이 일반적으로 많은 행을 리턴 할 수 있기 때문에 일종의 결과 콜렉션 또는 반복자 오브젝트를 작성해야 할 수도 있습니다. get ()으로 이것을 무시할 것입니다.
마지막으로 첫 번째 옵션은 짧고 여분의 임시 변수를 생략합니다. 약간의 차이 만 있지만 모든 작은 도움이됩니다.
왜 모든 것이 작동합니까? 4 줄을 하나의 내장 단축키로 바꿉니다. (이것은 자체 시도 / 제외를 수행합니다.)
from django.shortcuts import get_object_or_404
obj = get_object_or_404(MyModel, id=1)
Some more info about exceptions. If they are not raised, they cost almost nothing. Thus if you know you are probably going to have a result, use the exception, since using a conditional expression you pay the cost of checking every time, no matter what. On the other hand, they cost a bit more than a conditional expression when they are raised, so if you expect not to have a result with some frequency (say, 30% of the time, if memory serves), the conditional check turns out to be a bit cheaper.
But this is Django's ORM, and probably the round-trip to the database, or even a cached result, is likely to dominate the performance characteristics, so favor readability, in this case, since you expect exactly one result, use get()
.
I've played with this problem a bit and discovered that the option 2 executes two SQL queries, which for such a simple task is excessive. See my annotation:
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
An equivalent version that executes a single query is:
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]
By switching to this approach, I was able to substantially reduce number of queries my application executes.
Interesting question, but for me option #2 reeks of premature optimisation. I'm not sure which is more performant, but option #1 certainly looks and feels more pythonic to me.
I suggest a different design.
If you want to perform a function on a possible result, you could derive from QuerySet, like this: http://djangosnippets.org/snippets/734/
The result is pretty awesome, you could for example:
MyModel.objects.filter(id=1).yourFunction()
Here, filter returns either an empty queryset or a queryset with a single item. Your custom queryset functions are also chainable and reusable. If you want to perform it for all your entries: MyModel.objects.all().yourFunction()
.
They are also ideal to be used as actions in the admin interface:
def yourAction(self, request, queryset):
queryset.yourFunction()
Option 1 is more elegant, but be sure to use try..except.
From my own experience I can tell you that sometimes you're sure there cannot possibly be more than one matching object in the database, and yet there will be two... (except of course when getting the object by its primary key).
참고URL : https://stackoverflow.com/questions/1018886/django-filter-versus-get-for-single-object
'development' 카테고리의 다른 글
가장 빠른 검색을 제공하는 .NET 컬렉션 (0) | 2020.06.28 |
---|---|
PHP로 URL 재 작성 (0) | 2020.06.28 |
요소에 공백이있는 배쉬 배열 (0) | 2020.06.28 |
JSON 객체의 항목이“json.dumps”를 사용하여 순서가 잘못 되었습니까? (0) | 2020.06.28 |
영문자와 만 일치하는 정규식 (0) | 2020.06.28 |