QuerySet의 특징
QuerySet의 메서드들을 정리하기전에, QuerySet의 특징을 정리하였다.
•
필터링된 쿼리셋은 고유하다. (Filtered QuerySets are unique)
각 QuerySet은 각 필터링이 저장될 때 마다 고유한 QuerySet을 저장할 수 있다.
q1 = Entry.objects.filter(age=20)
q2 = q1.exclude(country="china")
q3 = q1.filter(country="korea")
Python
복사
•
지연연산 (Lazy Evaluation)
각 필터링을 진행할 때 마다 DB에서 작업 후 QuerySet을 가져오는게 아닌, 실제로 데이터가 필요할 때만 DB작업이 이루어진다.
q = Entry.objects.filter(age=20) # 실제 DB 작업 X
q = q.exclude(country="china") # 실제 DB 작업 X
q = q.filter(country="korea") # 실제 DB 작업 X
print(q) # 실제 DB 작업 O
Python
복사
그렇다면 언제 DB 작업(evaluate)가 이루어질까?
1.
Slice로 Step 파라미터(:) 사용 시
Slice로 일부를 가져오는 것(LIMIT)가 가능하다.
음수 인덱싱은 지원하지 않는다. EX) Entry.objects.all()[-1]
# LIMIT 5
Entry.objects.all()[:5]
# OFFSET 5 LIMIT 5
Entry.objects.all()[5:10]
Python
복사
2.
pickling / caching
QuerySet을 피클 시 피클링되기전에 강제로 메모리에 로드되도록 한다.
3.
repr() 로 호출 시
4.
len() 으로 호출 시
갯수를 알아야 될 때는 count()를 사용하자
>>> e = Entry.objects.filter(blog__name='Beatles Blog')
>>> len(e)
2
Python
복사
5.
list()로 변환할 때
>>> e = Entry.objects.filter(blog__name='Beatles Blog')
>>> list(e)
[<Entry: TEST Headline>, <Entry: New Lennon Biography>]
Python
복사
6.
해당 구문이 boolean으로 사용되었을 때
존재여부를 확인할 때는 exists() 를 사용하면 된다.
EX) Entry.objects.filter(...).exists()
if Entry.objects.filter(name='TEST'):
...
Python
복사
사용 시 새로운 QuerySet을 반환하는 API
해당 메서드로 존재하는 객체가 있을 경우 QuerySet 형태로 반환한다.
>>> e = Blog.objects.filter(name='Beatles Blog2')
>>> e
<QuerySet [<Blog: Beatles Blog2>]>
Python
복사
모든 데이터 조회 [ all() ]
•
all() 메서드를 사용하면 데이터베이스에 있는 모든 개체의 QuerySet를 반환한다.
all_entries = Entry.objects.all()
Python
복사
필터를 사용한 특정 개체 조회 [ filter(), exclude() ]
•
filter(**kwargs) 메서드를 사용하면 원하는 조건과 일치하는 QuerySet을 반환한다.
# pk가 1인 QuerySet을 반환한다.
filter_entries = Entry.objects.filter(pk=1)
Python
복사
•
exclude(**kwargs) 메서드를 사용하면 설정한 조건과 일치하지 않는 QuerySet을 반환한다.
# pk가 1이 아닌 QuerySet을 반환한다.
filter_entries = Entry.objects.filter(pk=1)
Python
복사
•
필터링 메서드를 아래와 같이 여러개를 연결하여 사용할 수 있다.
# pk가 1이 아닌 QuerySet들 중에서 age값이 20인 QuerySet만 필터링
filter_entries = Entry.objects.exclude(pk=1).filter(age=20)
Python
복사
select_related(), prefetch_related()를 활용한 성능 향상
만약 select_related나 prefetch_related없이 related된 정보를 추가로 요청하게 된다면, 그만큼 불필요하거나 중복된 쿼리를 추가로 요청하게 된다.
select_related() / prefetch_related()를 사용하면 related된 모델의 오브젝트를 함께 요청할 수 있고 이 데이터는 캐싱된다.
related된 정보를 추가로 요청하게 된다면 추가 쿼리 요청 없이 캐싱된 related된 정보를 가져오기 때문에 추가적으로 DB에 쿼리가 줄어들기 때문에, 성능 향상을 기대할 수 있다.
•
정참조 : 내가 참조하는 객체에 대한 정보를 가져오는 것
•
역참조 : 나를 참조하는 객체에 대한 정보를 가져오는 것
select_related()
쿼리셋 반환 시 Foreign-key(정참조) / OneToOneFeild(역참조) 관계인 모델의 오브젝트를 함께 가져온다.
예시 모델
예시 [ foreign-key(정참조) ]
예시 [ OneToOneFeild(역참조) ]
prefetch_related()
쿼리셋 반환 시 foreign-key(역참조) / Many-to-Many 또는 One-to-Many(정참조) 관계인 모델의 오브젝트를 함께 가져온다.
_set 은 역참조를 위한 예약어로, 만약 별도의 related_name을 지정하지 않은 상태에서 이를 넣어주지 않으면 역참조가 불가능하다.
예시 모델
예시 ORM 코드 [ foreign-key(역참조) ]
예시 ORM 코드 [ Many-to-Many(정참조) ]
사용 시 새로운 QuerySet을 반환하지 않는 API
해당 메서드로 존재하는 객체가 있을 경우 해당 객체만 반환한다.
>>> e = Blog.objects.get(name='Beatles Blog2')
>>> e
<Blog: Beatles Blog2>
Python
복사
단일 객체 조회 [ get() ]
get() 메서드를 사용하면 단일 개체만 조회가 가능하다.
만약 여러개의 객체가 존재하는 조건을 사용하면 MultipleObjectsReturned를 발생시킨다.
다중 개체를 조회하기 위해서는 filter()를 사용해야 되며, filter() 메서드는 조건에 맞는 객체가 하나일 때도 QuerySet 형태로 반환한다.
# age 값이 20인 단일 객체 조회
q = Entry.objects.get(age=20)
# 바로 조회 가능한 객체 반환
>>> q.age
20
...
Python
복사