Django에서 OR 쿼리 필터를 동적으로 작성하는 방법은 무엇입니까?


104

예에서 다중 OR 쿼리 필터를 볼 수 있습니다.

Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))

예를 들어, 결과는 다음과 같습니다.

[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]

그러나 목록에서이 쿼리 필터를 만들고 싶습니다. 그렇게하는 방법?

예 : [1, 2, 3] -> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))


1
당신은 이것을 두 번 요청한 것 같습니다 : stackoverflow.com/questions/852404
Dominic Rodger

이 특정 사용 사례의 경우 아마도 Article.objects.filter(pk__in=[1, 2, 3])현대 django에서 사용할 수 있지만 Q 객체를 함께 OR로 연결하여 좀 더 고급 작업을 수행하려는 경우 질문은 여전히 ​​관련이 있습니다.
beruic

답변:


162

다음과 같이 쿼리를 연결할 수 있습니다.

values = [1,2,3]

# Turn list of values into list of Q objects
queries = [Q(pk=value) for value in values]

# Take one Q object from the list
query = queries.pop()

# Or the Q object with the ones remaining in the list
for item in queries:
    query |= item

# Query the model
Article.objects.filter(query)

3
감사! 이것은 내가 찾던 것입니다 :) 당신이 할 수 있다는 것을 몰랐습니다 | =
Jack Ha

23
다음을 사용하여 쿼리를 초기화 할 수도 있습니다. query = Q ()
chachan

5
: 당신이 {값 "필드 명"**}를 사용하여 동적 필드를 만들 수있다 : = 값의 Q 값 ({값 '필드 이름은'} **)] 질의
rechie

1
위와 같은 선택적 조건을 추가하려면 Django로 원시 쿼리를 어떻게 작성할 수 있습니까?
사용자

그것은 저에게 효과가 없었습니다. 이유를 모르겠습니다. 쿼리는 나를 위해 제로 결과를 반환
MEHRAN 누리

83

더 복잡한 쿼리를 작성하려면 내장 된 Q () 객체의 상수 Q.OR 및 Q.AND를 다음과 같이 add () 메서드와 함께 사용하는 옵션도 있습니다.

list = [1, 2, 3]
# it gets a bit more complicated if we want to dynamically build
# OR queries with dynamic/unknown db field keys, let's say with a list
# of db fields that can change like the following
# list_with_strings = ['dbfield1', 'dbfield2', 'dbfield3']

# init our q objects variable to use .add() on it
q_objects = Q(id__in=[])

# loop trough the list and create an OR condition for each item
for item in list:
    q_objects.add(Q(pk=item), Q.OR)
    # for our list_with_strings we can do the following
    # q_objects.add(Q(**{item: 1}), Q.OR)

queryset = Article.objects.filter(q_objects)

# sometimes the following is helpful for debugging (returns the SQL statement)
# print queryset.query

12
나처럼이 스레드에 새로 온 사람들에게는이 답변이 최고의 답변으로 간주되어야한다고 생각합니다. 받아 들여지는 대답보다 Djangoesque입니다. 감사합니다!
theresaanna

5
내장 OR 및 AND 연산자 (| 및 &)를 사용하는 것이 더 비단뱀 적이라고 토론 할 것입니다. q_objects |= Q(pk=item)
Bobort 2016-08-24

완전한! 감사합니다!
RL Shyam

1
list비어있는 경우 Article.objects.all(). Article.objects.none()그래도 해당 테스트 로 돌아가서 완화하기 쉽습니다 .
Wil

2
당신은 또한 초기화 할 수 있습니다 @Wil q_objects와 함께 Q(id__in=[]). 무언가와 OR되지 않으면 항상 실패하고 쿼리 최적화 프로그램이 잘 처리합니다.
Jonathan Richards

44

Python의 reduce 기능을 사용하여 Dave Webb의 답변을 작성하는 더 짧은 방법 :

# For Python 3 only
from functools import reduce

values = [1,2,3]

# Turn list of values into one big Q objects  
query = reduce(lambda q,value: q|Q(pk=value), values, Q())  

# Query the model  
Article.objects.filter(query)  

"내장"감소가 제거되고로 대체 된 것 같습니다 functools.reduce. 출처
lsowen

감사합니다 @lsowen, 고정.
Tom Viner 2017 년

그리고 operator.or_람다 대신 사용할 수 있습니다 .
eigenein

38
from functools import reduce
from operator import or_
from django.db.models import Q

values = [1, 2, 3]
query = reduce(or_, (Q(pk=x) for x in values))

좋아,하지만 어디에서 operator왔어?
mpiskore

1
@mpiskore : 다른 모든 Python 모듈과 같은 위치 : 가져옵니다.
Ignacio Vazquez-Abrams

1
이상한. 정말 내 질문이었습니다. 어떤 모듈 / 라이브러리에서 찾을 수 있습니까? Google은별로 도움이되지 않았습니다.
mpiskore

오, 나는 그것이 일종의 Django ORM 연산자라고 생각했습니다. 나 얼마나 어리석은가, 고마워!
mpiskore

20

SQL IN 문을 사용하는 것이 더 낫습니다.

Article.objects.filter(id__in=[1, 2, 3])

참조 의 검색어 API를 참조 .

동적 논리를 사용하여 쿼리를 작성해야하는 경우 다음과 같이 할 수 있습니다 (추악 + 테스트되지 않음).

query = Q(field=1)
for cond in (2, 3):
    query = query | Q(field=cond)
Article.objects.filter(query)

1
당신은 또한 사용할 수 있습니다query |= Q(field=cond)
Bobort 2016-08-24

8

문서 참조 :

>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}

이 방법은 기본 키 조회에만 작동하지만 수행하려는 작업 인 것 같습니다.

그래서 당신이 원하는 것은 :

Article.objects.in_bulk([1, 2, 3])

6

쿼리 할 db 필드를 프로그래밍 방식으로 설정하려는 경우 :

import operator
questions = [('question__contains', 'test'), ('question__gt', 23 )]
q_list = [Q(x) for x in questions]
Poll.objects.filter(reduce(operator.or_, q_list))

6

reduceor_연산자를 사용 하여 필드를 곱하여 필터링하는 솔루션입니다 .

from functools import reduce
from operator import or_
from django.db.models import Q

filters = {'field1': [1, 2], 'field2': ['value', 'other_value']}

qs = Article.objects.filter(
   reduce(or_, (Q(**{f'{k}__in': v}) for k, v in filters.items()))
)

ps f는 새로운 형식 문자열 리터럴입니다. 파이썬 3.6에서 소개되었습니다.


4

| = 연산자를 사용하여 Q 객체를 사용하여 쿼리를 프로그래밍 방식으로 업데이트 할 수 있습니다.


2
문서화되어 있습니까? 나는 지난 15 분 동안 검색해 왔는데 이것이 내가 찾을 수있는 유일한 것입니다.
wobbily_col 2011 년

우리 업계와 마찬가지로 StackOverflow에 문서화되어 있습니다!
Chris

2

이것은 동적 pk 목록입니다.

pk_list = qs.values_list('pk', flat=True)  # i.e [] or [1, 2, 3]

if len(pk_list) == 0:
    Article.objects.none()

else:
    q = None
    for pk in pk_list:
        if q is None:
            q = Q(pk=pk)
        else:
            q = q | Q(pk=pk)

    Article.objects.filter(q)

q = Q()대신을 사용한 q = None다음 if q is None절 을 제거 할 수 있습니다. 약간 덜 효율적이지만 세 줄의 코드를 제거 할 수 있습니다. (빈 Q는 쿼리가 실행될 때 나중에 병합됩니다.)
Chris

1

또 다른 옵션은 최근까지 인식되지 않았습니다 - QuerySet또한 무시 &, |, ~, 등, 연산자. OR Q 객체가이 질문에 대한 더 나은 해결책이라는 다른 답변이지만 관심 / 인수를 위해 다음을 수행 할 수 있습니다.

id_list = [1, 2, 3]
q = Article.objects.filter(pk=id_list[0])
for i in id_list[1:]:
    q |= Article.objects.filter(pk=i)

str(q.query)WHERE절의 모든 필터가 포함 된 하나의 쿼리를 반환합니다 .


1

For 루프 :

values = [1, 2, 3]
q = Q(pk__in=[]) # generic "always false" value
for val in values:
    q |= Q(pk=val)
Article.objects.filter(q)

줄이다:

from functools import reduce
from operator import or_

values = [1, 2, 3]
q_objects = [Q(pk=val) for val in values]
q = reduce(or_, q_objects, Q(pk__in=[]))
Article.objects.filter(q)

둘 다 다음과 같습니다. Article.objects.filter(pk__in=values)

values비어 있을 때 원하는 것을 고려하는 것이 중요합니다 . Q()시작 값으로 많은 답변 은 모든 것을 반환 합니다. Q(pk__in=[])더 나은 시작 값입니다. 최적화 프로그램에 의해 잘 처리되는 항상 실패하는 Q 객체입니다 (복잡한 방정식의 경우에도).

Article.objects.filter(Q(pk__in=[]))  # doesn't hit DB
Article.objects.filter(Q(pk=None))    # hits DB and returns nothing
Article.objects.none()                # doesn't hit DB
Article.objects.filter(Q())           # returns everything

당신이 경우 원하는 경우 모든 반환 values비어, 당신은 AND로해야 ~Q(pk__in=[])하는 동작을 보장하기 위해 :

values = []
q = Q()
for val in values:
    q |= Q(pk=val)
Article.objects.filter(q)                     # everything
Article.objects.filter(q | author="Tolkien")  # only Tolkien

q &= ~Q(pk__in=[])
Article.objects.filter(q)                     # everything
Article.objects.filter(q | author="Tolkien")  # everything

항상 성공하는 Q 객체가 아니라 아무것도 아니라는 것을 기억하는 Q()것이 중요 합니다. 이를 포함하는 모든 작업은 완전히 삭제됩니다.


0

easy ..
from django.db.models import Q import you model args = (Q (visibility = 1) | (Q (visibility = 0) & Q (user = self.user))) #Tuple parameters = {} #dic order = 'create_at'제한 = 10

Models.objects.filter(*args,**parameters).order_by(order)[:limit]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.