2008-11-22 29 views
121

Cho một lớp:Trong Django, làm thế nào để lọc một QuerySet với tra cứu trường động?

from django.db import models 

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

Có thể, và nếu như vậy như thế nào, để có một QuerySet rằng bộ lọc dựa trên lập luận độ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`. 

Trả lời

222

mở rộng tranh luận 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.

+5

Chỉ cần một đầu ghi chú nhanh: đảm bảo các chuỗi trong kwarg có kiểu str không phải unicode, nếu không filter() sẽ càu nhàu. –

+0

Sẽ không? Nó không coerce vào thời điểm này? – jMyles

+0

Cảm ơn Daniel! Nó đã giúp đỡ tôi. Nó được gọi như thế nào trong Python? Mở rộng đối số? Tôi không tìm thấy nó trong tài liệu. – santiagobasulto

-1

Một biểu mẫu 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 tìm đường.

Làm thế nào, chính xác, bạn có mong đợi nhận được các giá trị cho tên và hoạt động của cột không? Bạn nhận được các giá trị của 'name' một số 'startswith' ở đâu?

filter_by = '%s__%s' % ('name', 'startswith') 
  1. A "tìm kiếm" hình thức? Bạn sẽ - cái gì? - chọn tên từ danh sách tên? Chọn hoạt động từ danh sách các hoạt động? Trong khi mở kết thúc, 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? Danh sách chọn phức tạp không có ý nghĩa. Một vài trường 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. Có vẻ như "trường" thực sự là một khóa cho một hàng trong một bảng khác, không phải là một cột.
  2. Nút bộ lọc cụ thể. Đợi đã ... Đó là cách mà quản trị viên Django hoạt động. Các bộ lọc cụ thể được chuyể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 thông thường đầu tiên.

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

+0

Cảm ơn bạn đã trả lời. Mô hình dữ liệu được đưa ra như một ví dụ chỉ là, để minh hoạ dễ dàng. Tôi không muốn tưởng tượng bất cứ ai đặt bất cứ điều gì mà ghê tởm vào một dự án thực sự. ;) Tôi muốn tách rời các mối quan hệ chung và đưa ra một số logic tái sử dụng được. –

+0

Django đã * là * chung chung. Viết những thứ chung chung hơn trên đầu trang của Django là một chút quá nhiều genericity. Đề xuất của tôi là chỉ cần triển khai ứng dụng của bạn, tránh việc khái quát hóa một khung công tác chung chung. –

+8

Đối với sự tôn trọng, nó là giả thiết để đưa ra các khuyến nghị mà không cần biết gì về thiết kế. Để "đơn giản thực hiện" ứng dụng này sẽ quên đi các chức năng thiên văn (> 200 apps^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. :) –

6

Ví dụ đơn giản:

Trong ứng dụng khảo sát Django, tôi muốn 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 tiêu chí truy vấn (chẳng hạn như chỉ những người đã hoàn thành một hội thảo nhất định). Để phần tử khảo sát có thể sử dụng lại được, 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 mà tôi đưa ra không phải là thân thiện với người dùng 100% (yêu cầu trợ giúp từ một người công nghệ để 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ó quay trở lại ở dạng self.question.custom_query. Giá trị của số đó là một chuỗi mà trông như một từ điển.Chúng tôi biến nó trở thành một thực điển với eval() và sau đó nhét nó vào queryset với kwargs **:

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

Tôi tự hỏi điều gì sẽ tạo ra một ModelField/FormField/WidgetField tùy chỉnh đã triển khai hành vi để cho phép người dùng, ở 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. Âm thanh như một dự án gọn gàng ... –

+1

T. Đá - Tôi tưởng tượng sẽ dễ dàng xây dựng một công cụ như vậy theo 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 một cách triệt để tất cả các tùy chọn có thể, đặc biệt nếu các mô hình phức tạp. – shacker

+2

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

6

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

+5

Bạn (hoặc ai đó) có thể cung cấp 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

Cũng giống như trong câu trả lời của [Câu trả lời của Daniel Naab] (http://stackoverflow.com/a/310785/4021086) 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 trong một danh sách và sử dụng '.filter (* q_objects)' hoặc sử dụng toán tử bitwise để kết hợp các đối tượng Q. –

Các vấn đề liên quan