장고를 통해 쿼리를 제대로 조작 하기 위해서는 필터를 잘 다룰 수 있어야한다. 이번에 사용하면서 깨달았던 부분들을 정리하려 한다.
첫번째로 Q 오브젝트이다. 사용하면서 깨달은 주의사항을 나열해 보려 한다.
Q 오브젝트는 항상 필터의 앞부분에서만 적용 가능하다.
예를 들어서 블로그라는 모델이 있는데 블로그가 닫혀있지않으면서 요청한 유저가 포함되지 않은 쿼리를 장고 orm을 활용해서 작성한다면 아래와 같다.
from django.db.models.query_utils import Q
from blog.models import Blog
Blog.objects.filter(is_closed=True, ~Q(user=request.user))
하지만 실행하면 오류를 뿜어내는 모습을 볼 수 있었다. 같은 표현이지만 순서만 변경해본다.
from django.db.models.query_utils import Q
from blog.models import Blog
Blog.objects.filter(~Q(user=request.user), is_closed=True)
같은 표현이지만 Q가 항상 filter 인자의 앞부분에 배치함으로써 에러가 해결된다. 구체적으로 아직 알아보진 않았지만 함수 구조적으로 발생한 오류인것 같다.
from django.db.models.query_utils import Q
from blog.models import Blog
Blog.objects.filter(~(Q=user=request.user), is_closed=True).\
filter(post_set__user=~Q(user=request.user)).\
filter(post_set__created_date__lte=timezone.now())
위와 같이 코드를 입력한다면 아래와 같은 쿼리문을 볼 수 있다.
SELECT * // 생략입니다.
FROM blog_blog AS b // 편의상 별칭 사용 조건에 집중해주세요
INNER JOIN blog_post
ON (b.id = blog_post.id)
INNER JOIN blog_post AS T3
ON( b.id = T3.id )
WHERE ..... // 조건 생략
불필요한 조인이 2번 되는 것을 볼 수 있으며 따라서 우리가 의도한 값이 나오지 않는다. 따라서 원하는 결과 값을 얻기 위해서는 하나의 filter 에서 모두 완성해야 한다.
from django.db.models.query_utils import Q
from blog.models import Blog
Blog.objects.filter(~(Q=user=request.user), is_closed=True).\
filter(post_set__user=~Q(user=request.user), post_set__created_date__lte=timezone.now())
위와 같이 작성했다면 한번의 조인으로 원하는 결과값을 도출해 낼 수 있다.
SELECT * // 생략입니다.
FROM blog_blog AS b // 편의상 별칭 사용 조건에 집중해주세요
INNER JOIN blog_post
ON (b.id = blog_post.id)
WHERE ..... // 조건 생략
3번 처럼 하나의 필터에 모든 조건을 넣게 된다면 너무 길어져서 가독성이 나빠질 가능성도 있고 혹은 동적으로 조건을 생성해야 하는 경우도 발생 할 수 있다. 이 경우에 Q 오브젝트를 미리 생성 한 후 add 메서드를 통해 조건을 추가할 수 있다.
from django.db.models.query_utils import Q
q = Q()
q.add(~Q(post_set__user), q.OR) //q.add(data, content_type, squash)