장고 필터

이코딩·2020년 4월 14일
1

Django

목록 보기
1/1

Django filter

장고를 통해 쿼리를 제대로 조작 하기 위해서는 필터를 잘 다룰 수 있어야한다. 이번에 사용하면서 깨달았던 부분들을 정리하려 한다.

1. Q 오브젝트

첫번째로 Q 오브젝트이다. 사용하면서 깨달은 주의사항을 나열해 보려 한다.

  1. 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 인자의 앞부분에 배치함으로써 에러가 해결된다. 구체적으로 아직 알아보진 않았지만 함수 구조적으로 발생한 오류인것 같다.

  1. 보통 filter 함수를 사용할 때 가독성 부분을 생각해서 조건 하나당 filter 하나를 사용했었다. 하지만 외부 테이블을 필터링하는 경우라면 하나의 필터에서 전부 해결해야 한다. (예를들어 블로그와 1:N 관계를 가지는 상태에서 블로그가 역참조(related_set)하여 필터링 하는 경우 )
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 ..... // 조건 생략
  1. Q 오브젝트는 별도로 인스턴스화 하여 조건문을 생성할 수 있다.

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)
profile
Python Developer

0개의 댓글