Làm cách nào để tạo động một bộ lọc truy vấn HOẶC trong Django?


104

Từ một ví dụ, bạn có thể thấy nhiều bộ lọc truy vấn HOẶC:

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

Ví dụ, điều này dẫn đến:

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

Tuy nhiên, tôi muốn tạo bộ lọc truy vấn này từ một danh sách. Làm thế nào để làm điều đó?

ví dụ [1, 2, 3] -> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))


1
Có vẻ như bạn đã hỏi điều này hai lần: stackoverflow.com/questions/852404
Dominic Rodger

Đối với trường hợp sử dụng cụ thể này, bạn có thể sử dụng Article.objects.filter(pk__in=[1, 2, 3])trong django hiện đại, nhưng câu hỏi vẫn có liên quan nếu bạn muốn làm điều gì đó nâng cao hơn một chút bằng cách HOẶC Q các đối tượng lại với nhau.
beruic

Câu trả lời:


162

Bạn có thể xâu chuỗi các truy vấn của mình như sau:

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
Cảm ơn! Đây là những gì tôi đang tìm kiếm :) Không biết bạn có thể làm được | =
Jack Ha

23
Bạn cũng có thể khởi tạo các truy vấn sử dụng: query = Q ()
chachan

5
bạn có thể tạo các trường động bằng cách sử dụng ** {'fieldname': value}: queries = [Q (** {'fieldname': value}) cho giá trị trong các giá trị]
rechie 11/09/13

1
Làm thế nào bạn có thể soạn các truy vấn thô với Django nếu bạn muốn thêm các điều kiện tùy chọn như trên?
người dùng

Điều đó không hiệu quả với tôi, tôi không biết tại sao. truy vấn trả về 0 kết quả cho tôi
Mehran Nouri

83

Để xây dựng các truy vấn phức tạp hơn, cũng có tùy chọn sử dụng các hằng số QOR và Q.AND của đối tượng Q () cùng với phương thức add () như sau:

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
Đối với những người mới tham gia chủ đề này, như bản thân tôi, tôi nghĩ câu trả lời này nên được coi là câu trả lời hàng đầu. Nó là Djangoesque hơn là câu trả lời được chấp nhận. Cảm ơn bạn!
theresaanna

4
Tôi sẽ tranh luận rằng việc sử dụng các toán tử OR và AND nội trang (| và &) sẽ khó hiểu hơn. q_objects |= Q(pk=item)
Bobort

Hoàn hảo! Cảm ơn bạn!
RL Shyam

1
Cần lưu ý rằng nếu listxảy ra trống, bạn sẽ trả về giá trị tương đương Article.objects.all(). Tuy nhiên, dễ dàng giảm thiểu bằng cách quay lại Article.objects.none()kiểm tra đó.
Wil

2
@Wil bạn cũng có thể khởi tạo q_objectsbằng Q(id__in=[]). Nó sẽ luôn không thành công trừ khi ORed với một cái gì đó và trình tối ưu hóa truy vấn sẽ xử lý nó một cách tốt đẹp.
Jonathan Richards

44

Một cách ngắn hơn để viết câu trả lời của Dave Webb bằng cách sử dụng hàm giảm của python :

# 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)  

Có vẻ như phần giảm "nội trang" đã bị xóa và thay thế bằng functools.reduce. nguồn
lsowen

Cảm ơn @lsowen, đã sửa.
Tom Viner

Và nó có thể sử dụng operator.or_thay vì lambda.
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))

Ok, nhưng nó operatorđến từ đâu?
mpiskore

1
@mpiskore: Giống với mọi mô-đun Python khác: bạn nhập nó.
Ignacio Vazquez-Abrams

1
buồn cười. đó thực sự là câu hỏi của tôi: tôi có thể tìm thấy nó trong mô-đun / thư viện nào? google đã không giúp được nhiều.
mpiskore

ồ, tôi nghĩ đó là một số loại toán tử Django ORM. Thật ngớ ngẩn của tôi, cảm ơn!
mpiskore

20

Có lẽ tốt hơn nên sử dụng câu lệnh sql IN.

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

Xem tham chiếu api của queryset .

Nếu bạn thực sự cần thực hiện các truy vấn với logic động, bạn có thể làm điều gì đó như sau (xấu xí + không được kiểm tra):

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

1
Bạn cũng có thể sử dụngquery |= Q(field=cond)
Bobort

8

Xem tài liệu :

>>> 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([])
{}

Lưu ý rằng phương pháp này chỉ hoạt động đối với tra cứu khóa chính, nhưng đó dường như là những gì bạn đang cố gắng thực hiện.

Vì vậy, những gì bạn muốn là:

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

6

Trong trường hợp chúng tôi muốn lập trình trường db mà chúng tôi muốn truy vấn:

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

Giải pháp sử dụng reduceor_toán tử để lọc theo các trường nhân.

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 flà một chuỗi định dạng mới theo nghĩa đen. Nó đã được giới thiệu trong python 3.6


4

Bạn có thể sử dụng toán tử | = để cập nhật lập trình truy vấn bằng Q đối tượng.


2
Điều này có được ghi lại ở bất cứ đâu không? Tôi đã tìm kiếm trong 15 phút qua và đây là thứ duy nhất tôi có thể tìm thấy.
wobbily_col Ngày

Giống như rất nhiều thứ trong ngành của chúng tôi, nó được ghi lại trên StackOverflow!
Chris

2

Cái này dành cho danh sách pk động:

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)

Bạn có thể sử dụng q = Q()thay vì q = None, sau đó loại bỏ if q is Nonemệnh đề - hơi kém hiệu quả hơn nhưng có thể loại bỏ ba dòng mã. (Q trống sau đó được hợp nhất đi khi truy vấn được chạy.)
Chris

1

Một lựa chọn khác tôi đã không nhận thức được đến gần đây - QuerySetcũng đè &, |, ~, vv, các nhà khai thác. Các câu trả lời khác mà đối tượng OR Q là một giải pháp tốt hơn cho câu hỏi này, nhưng vì lợi ích / lập luận, bạn có thể làm:

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)sẽ trả về một truy vấn với tất cả các bộ lọc trong WHEREmệnh đề.


1

Vòng lặp 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)

Giảm:

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)

Cả hai điều này đều tương đương với Article.objects.filter(pk__in=values)

Điều quan trọng là phải xem xét những gì bạn muốn khi valuestrống rỗng. Nhiều câu trả lời có Q()giá trị bắt đầu sẽ trả về mọi thứ . Q(pk__in=[])là một giá trị khởi đầu tốt hơn. Đó là một đối tượng Q luôn gặp lỗi được trình tối ưu hóa xử lý độc đáo (ngay cả đối với các phương trình phức tạp).

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

Nếu bạn muốn trả lại mọi thứ khi valuestrống, bạn nên VÀ với ~Q(pk__in=[])để đảm bảo rằng hành vi:

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

Điều quan trọng cần nhớ Q()khônggì cả , không phải là đối tượng Q luôn thành công. Bất kỳ hoạt động nào liên quan đến nó sẽ chỉ thả nó hoàn toàn.


0

dễ dàng ..
từ django.db.models nhập Q nhập mô hình args = (Q (khả năng hiển thị = 1) | (Q (khả năng hiển thị = 0) & Q (người dùng = self.user))) #Tuple thông số = {} #dic order = 'create_at' giới hạn = 10

Models.objects.filter(*args,**parameters).order_by(order)[:limit]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.