sự khác biệt giữa bộ lọc có nhiều đối số và bộ lọc chuỗi trong django


Câu trả lời:


60

Như bạn có thể thấy trong các câu lệnh SQL được tạo, sự khác biệt không phải là "OR" như một số người có thể nghi ngờ. Đó là cách đặt WHERE và JOIN.

Ví dụ1 (cùng một bảng được kết hợp): từ https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships

Blog.objects.filter(
       entry__headline__contains='Lennon', 
       entry__pub_date__year=2008)

Điều này sẽ cung cấp cho bạn tất cả các Blog có một mục nhập với cả hai (entry__headline__contains='Lennon') AND (entry__pub_date__year=2008), đó là những gì bạn mong đợi từ truy vấn này.

Kết quả:

Blog with {entry.headline: 'Life of Lennon', entry.pub_date: '2008'}

Ví dụ 2 (chuỗi)

Blog.objects.filter(
       entry__headline__contains='Lennon'
           ).filter(
       entry__pub_date__year=2008)

Điều này sẽ bao gồm tất cả các kết quả từ Ví dụ 1, nhưng nó sẽ tạo ra nhiều kết quả hơn một chút. Bởi vì trước tiên nó lọc tất cả các blog có (entry__headline__contains='Lennon')và sau đó từ các bộ lọc kết quả (entry__pub_date__year=2008).

Sự khác biệt là nó cũng sẽ cho bạn kết quả như:

Một Blog có nhiều mục

{entry.headline: '**Lennon**', entry.pub_date: 2000}, 
{entry.headline: 'Bill', entry.pub_date: **2008**}

Khi bộ lọc đầu tiên được đánh giá, sách được bao gồm vì mục nhập đầu tiên (mặc dù sách có các mục nhập khác không khớp). Khi bộ lọc thứ hai được đánh giá, sách được bao gồm vì mục nhập thứ hai.

Một bảng: Nhưng nếu truy vấn không liên quan đến các bảng đã kết hợp như ví dụ từ Yuji và DTing. Kết quả là như nhau.


20
Tôi cho rằng tôi vừa mới dày đặc sáng nay, nhưng câu này khiến tôi bối rối: "Bởi vì nó đầu tiên lọc tất cả các blog có (entry__headline__contains='Lennon')và sau đó từ kết quả lọc (entry__pub_date__year=2008)" Nếu "thì từ kết quả" là chính xác, tại sao nó sẽ bao gồm một cái gì đó với entry.headline == 'Bill'.. .sẽ không entry__headline__contains='Lennon'lọc ra Billtrường hợp?
Dustin Wyatt

7
Tôi cũng đang bối rối. Nó có vẻ như câu trả lời này chỉ là sai, nhưng nó có 37 upvotes ...
Personman

1
Câu trả lời này gây hiểu lầm và khó hiểu, lưu ý phần trên chỉ đúng khi lọc bằng cách sử dụng các mối quan hệ M2M như đã lưu ý trong câu trả lời của Yuji. Điểm mấu chốt là ví dụ đang lọc các mục Blog với mỗi câu lệnh lọc, chứ không phải các mục Mục nhập.
theannouncer

1
Bởi vì có thể có nhiều mục trên mỗi blog. Ngôn ngữ là chính xác. Khái niệm này có thể gây nhầm lẫn nếu bạn không ghi nhớ tất cả các phần chuyển động.
DylanYoung

@DustinWyatt Tôi cũng có cùng câu hỏi như bạn nhưng cuối cùng tôi đã hiểu! Vui lòng xem ví dụ về Nhân viên và Phụ thuộc được viết bởi Grijesh Chauhan dưới đây trên trang này và bạn cũng sẽ hiểu.
theQuestionMan

33

Trường hợp kết quả của "nhiều đối số bộ lọc-truy vấn" khác với "chuỗi-bộ lọc-truy vấn", như sau:

Lựa chọn đối tượng được tham chiếu trên cơ sở đối tượng tham chiếu và mối quan hệ là một-nhiều (hoặc nhiều-nhiều).

Nhiều bộ lọc:

    Referenced.filter(referencing1_a=x, referencing1_b=y)
    #  same referencing model   ^^                ^^

Bộ lọc chuỗi:

    Referenced.filter(referencing1_a=x).filter(referencing1_b=y)

Cả hai truy vấn có thể xuất ra kết quả khác nhau:
Nếu nhiều hơn thì một hàng trong mô hình Referencing1tham chiếu có thể tham chiếu đến cùng một hàng trong mô hình được tham chiếu Referenced. Điều này có thể xảy ra trong Referenced: Referencing1có 1: N (một đến nhiều) hoặc N: M (nhiều đến nhiều)-ship.

Thí dụ:

Hãy xem xét ứng dụng của tôi my_companycó hai mô hình EmployeeDependent. Một nhân viên trong my_companycó thể có nhiều hơn người phụ thuộc (nói cách khác, một người phụ thuộc có thể là con trai / con gái của một nhân viên duy nhất, trong khi một nhân viên có thể có nhiều hơn một con trai / con gái).
Ơ, giả sử như vợ chồng cả hai đều không thể làm việc a my_company. Tôi lấy ví dụ 1: m

Vì vậy, Employeelà mô hình tham chiếu có thể được tham chiếu bởi nhiều hơn thì Dependentđó là mô hình tham chiếu. Bây giờ hãy xem xét trạng thái quan hệ như sau:

Employee:        Dependent:
+------+        +------+--------+-------------+--------------+
| name |        | name | E-name | school_mark | college_mark |
+------+        +------+--------+-------------+--------------+
| A    |        | a1   |   A    |          79 |           81 |
| B    |        | b1   |   B    |          80 |           60 |
+------+        | b2   |   B    |          68 |           86 |
                +------+--------+-------------+--------------+  

Phụ thuộc a1đề cập đến nhân viên A, và phụ thuộc b1, b2đề cập đến nhân viên B.

Bây giờ truy vấn của tôi là:

Tìm tất cả nhân viên có con trai / con gái có điểm phân biệt (giả sử> = 75%) ở cả trường đại học và trường học?

>>> Employee.objects.filter(dependent__school_mark__gte=75,
...                         dependent__college_mark__gte=75)

[<Employee: A>]

Đầu ra là 'A' phụ thuộc 'a1' có điểm phân biệt ở cả trường đại học và trường học phụ thuộc vào nhân viên 'A'. Lưu ý 'B' không được chọn vì em của 'B' có điểm phân biệt ở cả trường đại học và trường học. Đại số quan hệ:

Nhân viên (school_mark> = 75 AND college_mark> = 75) Phụ thuộc

Trong trường hợp thứ hai, tôi cần một truy vấn:

Tìm tất cả nhân viên có một số người phụ thuộc có điểm khác biệt trong trường đại học và trường học?

>>> Employee.objects.filter(
...             dependent__school_mark__gte=75
...                ).filter(
...             dependent__college_mark__gte=75)

[<Employee: A>, <Employee: B>]

Lần này "B" cũng được chọn vì "B" có hai đứa con (nhiều hơn một!), Một đứa có điểm phân biệt ở trường "b1" và đứa còn lại có điểm phân biệt ở trường đại học "b2".
Thứ tự của bộ lọc không quan trọng, chúng tôi cũng có thể viết truy vấn ở trên là:

>>> Employee.objects.filter(
...             dependent__college_mark__gte=75
...                ).filter(
...             dependent__school_mark__gte=75)

[<Employee: A>, <Employee: B>]

kết quả là như nhau! Đại số quan hệ có thể là:

(Nhân viên (điểm trường> = 75) Phụ thuộc) (dấu đại học> = 75) Phụ thuộc

Lưu ý sau:

dq1 = Dependent.objects.filter(college_mark__gte=75, school_mark__gte=75)
dq2 = Dependent.objects.filter(college_mark__gte=75).filter(school_mark__gte=75)

Kết quả tương tự: [<Dependent: a1>]

Tôi kiểm tra truy vấn SQL mục tiêu được tạo bởi Django bằng cách sử dụng print qd1.queryprint qd2.querycả hai đều giống nhau (Django 1.6).

Nhưng về mặt ngữ nghĩa cả hai đều khác nhau đối với tôi . đầu tiên trông giống như phần đơn giản σ [school_mark> = 75 VÀ college_mark> = 75] (Phụ thuộc) và thứ hai giống như truy vấn lồng nhau chậm: σ [school_mark> = 75][college_mark> = 75] (Phụ thuộc)).

Nếu ai đó cần Mã @codepad

btw, nó được đưa ra trong tài liệu @ Kéo dài các mối quan hệ đa giá trị Tôi vừa thêm một ví dụ, tôi nghĩ nó sẽ hữu ích cho người mới.


4
Cảm ơn bạn vì lời giải thích hữu ích này, nó tốt hơn lời giải thích trong tài liệu không rõ ràng chút nào.
wim

1
Dấu hiệu cuối cùng về việc lọc Người phụ thuộc trực tiếp là cực kỳ hữu ích. Nó cho thấy rằng sự thay đổi trong kết quả chắc chắn chỉ xảy ra khi bạn trải qua một mối quan hệ nhiều người. Nếu bạn truy vấn trực tiếp một bảng, các bộ lọc chuỗi giống như việc lược hai lần.
Chris

20

Hầu hết thời gian, chỉ có một bộ kết quả khả thi cho một truy vấn.

Việc sử dụng cho các bộ lọc chuỗi khi bạn xử lý m2m:

Xem xét điều này:

# will return all Model with m2m field 1
Model.objects.filter(m2m_field=1) 

# will return Model with both 1 AND 2    
Model.objects.filter(m2m_field=1).filter(m2m_field=2) 

# this will NOT work
Model.objects.filter(Q(m2m_field=1) & Q(m2m_field=2))

Các ví dụ khác được hoan nghênh.


4
Một ví dụ khác: Nó không chỉ giới hạn m2m, điều này cũng có thể xảy ra với một-nhiều - với sự đảo ngược tra cứu ví dụ như sử dụng related_name trên ForeignKey
wim

Cảm ơn lời giải thích của bạn! Trước đó, tôi nghĩ rằng ví dụ cuối cùng và ví dụ thứ 2 là bằng nhau, vì vậy ví dụ cuối cùng không phù hợp với tôi (kết quả truy vấn sai) và tôi đã dành rất nhiều thời gian để tìm kiếm. Ví dụ thứ 2 rất hữu ích cho tôi. Cũng như Wim đã nói, điều này có thể sử dụng được với quan hệ một-nhiều đảo ngược như trong trường hợp của tôi.
zen11625

12

Sự khác biệt về hiệu suất là rất lớn. Hãy thử nó và xem.

Model.objects.filter(condition_a).filter(condition_b).filter(condition_c)

chậm một cách đáng ngạc nhiên so với

Model.objects.filter(condition_a, condition_b, condition_c)

Như đã đề cập trong ORM Django hiệu quả ,

  • QuerySets duy trì trạng thái trong bộ nhớ
  • Chuỗi kích hoạt nhân bản, sao chép trạng thái đó
  • Thật không may, QuerySets duy trì rất nhiều trạng thái
  • Nếu có thể, không chuỗi nhiều hơn một bộ lọc

8

Bạn có thể sử dụng mô-đun kết nối để xem các truy vấn sql thô để so sánh. Theo giải thích của Yuji, phần lớn chúng tương đương nhau như được hiển thị ở đây:

>>> from django.db import connection
>>> samples1 = Unit.objects.filter(color="orange", volume=None)
>>> samples2 = Unit.objects.filter(color="orange").filter(volume=None)
>>> list(samples1)
[]
>>> list(samples2)
[]
>>> for q in connection.queries:
...     print q['sql']
... 
SELECT `samples_unit`.`id`, `samples_unit`.`color`, `samples_unit`.`volume` FROM `samples_unit` WHERE (`samples_unit`.`color` = orange  AND `samples_unit`.`volume` IS NULL)
SELECT `samples_unit`.`id`, `samples_unit`.`color`, `samples_unit`.`volume` FROM `samples_unit` WHERE (`samples_unit`.`color` = orange  AND `samples_unit`.`volume` IS NULL)
>>> 

2

Nếu bạn kết thúc trên trang này tìm cách để tự động xây dựng một django queryset với nhiều bộ lọc loạt, nhưng bạn cần các bộ lọc để có những ANDloại thay vì OR, hãy xem xét sử dụng đối tượng Q .

Một ví dụ:

# First filter by type.
filters = None
if param in CARS:
  objects = app.models.Car.objects
  filters = Q(tire=param)
elif param in PLANES:
  objects = app.models.Plane.objects
  filters = Q(wing=param)

# Now filter by location.
if location == 'France':
  filters = filters & Q(quay=location)
elif location == 'England':
  filters = filters & Q(harbor=location)

# Finally, generate the actual queryset
queryset = objects.filter(filters)

Trong trường hợp if hoặc elif không được chuyển, biến bộ lọc sẽ là Không và khi đó bạn nhận được Lỗi TypeError: (các) loại toán hạng không được hỗ trợ cho &: 'NoneType' và 'Q'. Tôi đã khởi tạo bộ lọc bằng bộ lọc = Q ()
cwhisperer


-4

Có một sự khác biệt khi bạn có yêu cầu đối với đối tượng liên quan của mình, chẳng hạn

class Book(models.Model):
    author = models.ForeignKey(Author)
    name = models.ForeignKey(Region)

class Author(models.Model):
    name = models.ForeignKey(Region)

yêu cầu

Author.objects.filter(book_name='name1',book_name='name2')

trả về tập hợp trống

và yêu cầu

Author.objects.filter(book_name='name1').filter(book_name='name2')

trả về các tác giả có sách có cả 'name1' và 'name2'

để biết chi tiết, hãy xem https://docs.djangoproject.com/en/dev/topics/db/queries/#s-spanning-multi-valued-relationships


5
Author.objects.filter(book_name='name1',book_name='name2')thậm chí không phải là python hợp lệ, nó sẽ làSyntaxError: keyword argument repeated
wim

1
Book_name được định nghĩa chính xác ở đâu? Ý bạn là book_set__name?
DylanYoung
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.