Trong Django, làm thế nào để lọc một Bộ truy vấn với tra cứu trường động?


160

Cho một lớp:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=20)

Có thể không, và nếu vậy, làm thế nào để có một Bộ truy vấn lọc dựa trên các đối số động? Ví dụ:

 # Instead of:
 Person.objects.filter(name__startswith='B')
 # ... and:
 Person.objects.filter(name__endswith='B')

 # ... is there some way, given:
 filter_by = '{0}__{1}'.format('name', 'startswith')
 filter_value = 'B'

 # ... that you can run the equivalent of this?
 Person.objects.filter(filter_by=filter_value)
 # ... which will throw an exception, since `filter_by` is not
 # an attribute of `Person`.

Câu trả lời:


310

Mở rộng đối số của Python có thể được sử dụng để giải quyết vấn đề này:

kwargs = {
    '{0}__{1}'.format('name', 'startswith'): 'A',
    '{0}__{1}'.format('name', 'endswith'): 'Z'
}

Person.objects.filter(**kwargs)

Đây là một thành ngữ Python rất phổ biến và hữu ích.


6
Chỉ cần một cái đầu nhanh lên: đảm bảo các chuỗi trong kwargs có kiểu str không unicode, bộ lọc khác () sẽ càu nhàu.
Steve Jalim

1
@santiagobasulto Nó cũng được đề cập đến một tham số đóng gói / giải nén và các biến thể của chúng.
Daniel Naab

7
tốt đẹp, tốt đẹp, và đẹp !
Oscar Mederos

5
@DanielNaab nhưng điều này sẽ chỉ hoạt động trên các kwarg hoạt động trên bộ lọc điều kiện AND, bất kỳ thay thế nào cho điều kiện OR.
Prateek099

3
@prateek bạn luôn có thể sử dụng các đối tượng Q: stackoverflow.com/questions/13076822/NH
deecodameeko

6

Một ví dụ đơn giản:

Trong một ứng dụng khảo sát Django, tôi muốn có một danh sách chọn HTML hiển thị người dùng đã đăng ký. Nhưng vì chúng tôi có 5000 người dùng đã đăng ký, tôi cần một cách để lọc danh sách đó dựa trên các tiêu chí truy vấn (chẳng hạn như những người đã hoàn thành một hội thảo nhất định). Để yếu tố khảo sát có thể được sử dụng lại, tôi cần cho người tạo câu hỏi khảo sát để có thể đính kèm các tiêu chí đó vào câu hỏi đó (không muốn mã hóa truy vấn vào ứng dụng).

Giải pháp tôi đưa ra không thân thiện với người dùng 100% (cần có sự trợ giúp của người kỹ thuật để tạo truy vấn) nhưng nó giải quyết được vấn đề. Khi tạo câu hỏi, trình chỉnh sửa có thể nhập từ điển vào trường tùy chỉnh, ví dụ:

{'is_staff':True,'last_name__startswith':'A',}

Chuỗi đó được lưu trữ trong cơ sở dữ liệu. Trong mã xem, nó trở lại dưới dạng self.question.custom_query. Giá trị của nó là một chuỗi trông giống như một từ điển. Chúng tôi biến nó trở lại thành một từ điển thực sự với eval () và sau đó nhét nó vào bộ truy vấn với ** kwargs:

kwargs = eval(self.question.custom_query)
user_list = User.objects.filter(**kwargs).order_by("last_name")   

Tôi đang tự hỏi sẽ cần những gì để tạo ModelField / FormField / WidgetField tùy chỉnh thực hiện hành vi cho phép người dùng, về phía GUI, về cơ bản "xây dựng" một truy vấn, không bao giờ nhìn thấy văn bản thực tế, nhưng sử dụng giao diện để làm như vậy. Nghe có vẻ như một dự án gọn gàng ...
T. Stone

1
T. Stone - Tôi tưởng tượng sẽ dễ dàng xây dựng một công cụ như vậy một cách đơn giản nếu các mô hình cần truy vấn đơn giản, nhưng rất khó thực hiện theo cách triệt để phơi bày tất cả các tùy chọn có thể, đặc biệt là nếu các mô hình là phức tạp.
Shacker

5
-1 kêu gọi eval()nhập người dùng là một ý tưởng tồi, ngay cả khi bạn hoàn toàn tin tưởng người dùng của mình. Một trường JSON sẽ là một ý tưởng tốt hơn ở đây.
John Carter

5

Django.db.models.Q chính xác là những gì bạn muốn theo cách Django.


7
Bạn có thể (hoặc ai đó) cung cấp một ví dụ về cách sử dụng các đối tượng Q trong việc sử dụng tên trường động không?
jackdbernier

3
Nó giống như trong câu trả lời của Daniel Naab Sự khác biệt duy nhất là bạn chuyển các đối số vào hàm tạo đối tượng Q. Q(**filters), nếu bạn muốn tự động xây dựng các đối tượng Q, bạn có thể đặt chúng vào danh sách và sử dụng .filter(*q_objects)hoặc sử dụng các toán tử bitwise để kết hợp các đối tượng Q.
Will S

5
Câu trả lời này thực sự nên bao gồm một ví dụ về việc sử dụng Q để giải quyết vấn đề của OP.
pdoherty926

-2

Một hình thức tìm kiếm thực sự phức tạp thường chỉ ra rằng một mô hình đơn giản hơn đang cố gắng khai thác nó.

Làm thế nào, chính xác, bạn mong đợi để có được các giá trị cho tên cột và hoạt động? Nơi nào bạn nhận được các giá trị của 'name'một 'startswith'?

 filter_by = '%s__%s' % ('name', 'startswith')
  1. Một mẫu "tìm kiếm"? Bạn đang đi - cái gì? - Chọn tên từ danh sách tên? Chọn hoạt động từ một danh sách các hoạt động? Trong khi kết thúc mở, hầu hết mọi người thấy điều này khó hiểu và khó sử dụng.

    Có bao nhiêu cột có bộ lọc như vậy? 6? 12? 18?

    • Một vài? Một danh sách chọn phức tạp không có ý nghĩa. Một vài lĩnh vực và một vài câu lệnh if có ý nghĩa.
    • Một số lượng lớn? Mô hình của bạn không đúng. Nghe có vẻ như "trường" thực sự là một chìa khóa cho một hàng trong một bảng khác, không phải là một cột.
  2. Các nút lọc cụ thể. Đợi đã ... Đó là cách mà quản trị viên Django làm việc. Các bộ lọc cụ thể được biến thành các nút. Và phân tích tương tự như trên áp dụng. Một vài bộ lọc có ý nghĩa. Một số lượng lớn các bộ lọc thường có nghĩa là một loại vi phạm hình thức bình thường đầu tiên.

Rất nhiều trường tương tự thường có nghĩa là nên có nhiều hàng hơn và ít trường hơn.


9
Với sự tôn trọng, việc đưa ra khuyến nghị mà không biết gì về thiết kế là điều quá tự nhiên. Để "thực hiện đơn giản", ứng dụng này sẽ bỏ qua các chức năng thiên văn (> 200 ứng dụng ^ 21 foos) để đáp ứng các yêu cầu. Bạn đang đọc mục đích và ý định vào ví dụ; bạn không nên :)
Brian M. Hunt

2
Tôi gặp rất nhiều người cảm thấy rằng vấn đề của họ sẽ là tầm thường để giải quyết nếu chỉ có những thứ (a) chung chung hơn và (b) hoạt động theo cách họ tưởng tượng. Cách đó nằm ở sự thất vọng vô tận bởi vì mọi thứ không theo cách họ tưởng tượng. Tôi đã thấy quá nhiều thất bại xuất phát từ việc "sửa khung".
S.Lott

2
Mọi thứ hoạt động như mong đợi và mong muốn theo phản hồi của Daniel. Câu hỏi của tôi là về cú pháp, không phải thiết kế. Nếu tôi có thời gian để viết ra thiết kế, tôi đã làm điều đó. Tôi chắc chắn rằng đầu vào của bạn sẽ hữu ích, tuy nhiên đó không phải là một lựa chọn thực tế.
Brian M. Hunt

8
S.Lott, câu trả lời của bạn thậm chí không trả lời từ xa câu hỏi này. Nếu bạn không biết câu trả lời, xin vui lòng để lại câu hỏi. Đừng trả lời với lời khuyên thiết kế không được yêu cầu khi bạn hoàn toàn không có kiến ​​thức về thiết kế!
slypete

2
@slypete: Nếu thay đổi thiết kế sẽ loại bỏ vấn đề, thì vấn đề đã được giải quyết. Tiếp tục dọc theo con đường dựa trên một thiết kế kém là tốn kém và phức tạp hơn mức cần thiết. Giải quyết các vấn đề nguyên nhân gốc rễ tốt hơn là giải quyết các vấn đề khác xuất phát từ các quyết định thiết kế tồi. Tôi xin lỗi bạn không thích phân tích nguyên nhân gốc rễ. Nhưng khi một cái gì đó thực sự khó khăn, điều đó thường có nghĩa là bạn đang thử sai thứ bắt đầu.
S.Lott
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.