[Re:Django] 10. QuerySet Method (4)

Magit·2020년 5월 6일
0

Django

목록 보기
10/13

.filter() 메소드와 같이 쓰이는 .exists()

해당 메소드는 DB에서 filter를 통해 원하는 조건의 데이터 유무에 따라 True, False 를 반환해준다. QuerySet은 아니다. 특정 조건에 대한 이벤트나 로직을 처리할 때 많이 쓰인다.


.reverse()

역순으로 반환한다. 즉, 쿼리셋의 순서를 반대로 바꿔버린다.

my_queryset.reverse()[:5]

my_queryset이 기존에 갖고 있는 정렬 순서를 .reverse()가 반대로 바꾸고 처음부터 5개의 데이터를 슬라이싱한다. 이는 초기 데이터에서 마지막 5개를 꺼내는 효과가 있다.

.reverse()는 파이썬의 seq[-5:]과 같은 효과를 내지만, 장고에서는 슬라이싱에서 음수를 지원하지 않기에 같은 방식으로 동작하지 않는다. 또한 .reverse() 는 QuerySet이 순서가 정의되어 있을 때만 효과가 있다.(기본 순서를 정의하는 모델에 대해 쿼리하거나 order_by()를 사용할 때) 정의된 순서가 없는 경우에는 효과가 없다.


ForeignKey나 OneToOneField에 사용가능하다. 조금 더 복잡한 쿼리를 사용하지만 데이터베이스에 한 번만 접근하면 캐싱하기 때문에 퍼포먼스를 향상시킬 수 있다. select_related 는 쿼리를 실행할 때 추가적인 관련 객체 데이터를 선택해서 외래키 관계를 팔로우 할 QuerySet을 반환한다. 더 복잡한 쿼리를 갖고오도록 부추기지만, 외래키 관계를 사용할 경우 DB쿼리가 필요하지 않음을 의미한다.

# Hits the database.
e = Entry.objects.get(id=5)

# Hits the database again to get the related Blog object.
b = e.blog

위의 코드 두 줄은 실행하는 동안 각각 데이터베이스에 접근해야 한다.

# Hits the database.
e = Entry.objects.select_related('blog').get(id=5)

# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog

select_related 로 'blog' 필드에 한 번 접근했기에 이미 캐싱이 되었고, e.blog에 접근할 때 데이터베이스를 거치지 않아도 된다.

# 객체의 QuerySet과 select_related() 함께 사용해보자
from django.utils import timezone

# 공개 예정인 항목이 있는 모든 블로그 찾기
blogs = set()

for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog'):
# select_related()가 없으면 각각에 대한 데이터베이스 쿼리를 만 항목에 대한 관련 블로그를 가져오기 위해 각 루프 반복에 대한 DB 쿼리가 된다.
    blogs.add(e.blog)
#`filter()` 나 `select_related()` 의 연결 순서는 상관없다.

Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog')
Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())

외래키를 쿼리하는 것과 비슷한 방식으로 외래키를 따를 수 있다.

# models
from django.db import models

class City(models.Model):
    # ...
    pass

class Person(models.Model):
    # ...
    hometown = models.ForeignKey(
        City,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

class Book(models.Model):
    # ...
    author = models.ForeignKey(Person, on_delete=models.CASCADE)
Book.objects.select_related('author__hometown').get(id=4)
# 관련 Person과 관련 City가 캐시된다.
# author와 hometown 테이블에 조인하여 DB를 히트한다.
b = Book.objects.select_related('author__hometown').get(id=4)
p = b.author         # Doesn't hit the database.
c = p.hometown       # Doesn't hit the database.

# Without select_related()...
b = Book.objects.get(id=4)  # Hits the database.
p = b.author         # Hits the database.
c = p.hometown       # Hits the database.

이 경우도 마찬가지이다. 위에서는 select_related로 이미 author 객체 형태를 관리하는 모델에 접근이 이뤄져서 캐싱이 되었다. 이후에 author와 관련된 데이터에 대해서는 데이터베이스를 거치지 않아도 된다.

위에서도 설명했다싶이 select_related()는 ForeignKey나 OneToOneField 관계를 참조할 수 있다. 또한 select_related()을 전달된 필드 리스트에서 OneToOneField의 역방향도 참조할 수 있다. 즉, OneToOneField를 필드가 정의된 객체로 다시 이동할 수 있다.

>>> without_relations = queryset.select_related(None)
# QuerySet에서 select_related의 이전 호출에 의해 추가된
# 관련 필드 리스트를 지워야 하는 경우 매개 변수로 None을 전달한다.
# select_related를 체이닝하여 콜하는 경우
# 둘은 같다.
select_related('foo', 'bar') == select_related('foo').select_related('bar')
profile
이제 막 배우기 시작한 개발자입니다.

0개의 댓글