Lọc theo thuộc tính


95

Có thể lọc bộ truy vấn Django theo thuộc tính mô hình không?

tôi có một phương pháp trong mô hình của mình:

@property
def myproperty(self):
    [..]

và bây giờ tôi muốn lọc theo thuộc tính này như:

MyModel.objects.filter(myproperty=[..])

là điều này bằng cách nào đó có thể?


Nó có trong SQLAlchemy: docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html và bạn có thể kết nối django với SQLAlchemy qua pypi.python.org/pypi/aldjemy nhưng tôi nghi ngờ rằng cả hai có thể được kết nối theo cách bạn muốn chúng trở thành.
rắn chuông

Câu trả lời:


78

Không. Bộ lọc Django hoạt động ở cấp cơ sở dữ liệu, tạo ra SQL. Để lọc dựa trên thuộc tính Python, bạn phải tải đối tượng vào Python để đánh giá thuộc tính - và tại thời điểm đó, bạn đã thực hiện tất cả công việc để tải nó.


5
Thật không may là tính năng này không được triển khai, sẽ là một phần mở rộng thú vị để ít nhất là lọc ra các đối tượng phù hợp sau khi tập kết quả đã được xây dựng.
schneck,

1
làm thế nào để đối phó với nó trong quản trị? Có một số cách giải quyết?
andilabs

39

Tôi có thể hiểu sai câu hỏi ban đầu của bạn, nhưng có một nội dung bộ lọc trong python.

filtered = filter(myproperty, MyModel.objects)

Nhưng tốt hơn nên sử dụng cách hiểu danh sách :

filtered = [x for x in MyModel.objects if x.myproperty()]

hoặc thậm chí tốt hơn, một biểu thức trình tạo :

filtered = (x for x in MyModel.objects if x.myproperty())

15
Điều đó hoạt động để lọc nó khi bạn có một đối tượng Python, nhưng anh ấy đang hỏi về Django QuerySet.filter, công cụ tạo truy vấn SQL.
Glenn Maynard,

1
đúng, nhưng như đã giải thích ở trên, tôi muốn thêm thuộc tính vào bộ lọc cơ sở dữ liệu của mình. lọc sau khi truy vấn đã được thực hiện chính xác là những gì tôi muốn tránh.
schneck, 30-07-09

19

Ngoài cách giải quyết được đề xuất của @ TheGrimmScientist, bạn có thể tạo các "thuộc tính sql" này bằng cách xác định chúng trên Manager hoặc QuerySet và sử dụng lại / chain / compile chúng:

Với người quản lý:

class CompanyManager(models.Manager):
    def with_chairs_needed(self):
        return self.annotate(chairs_needed=F('num_employees') - F('num_chairs'))

class Company(models.Model):
    # ...
    objects = CompanyManager()

Company.objects.with_chairs_needed().filter(chairs_needed__lt=4)

Với QuerySet:

class CompanyQuerySet(models.QuerySet):
    def many_employees(self, n=50):
        return self.filter(num_employees__gte=n)

    def needs_fewer_chairs_than(self, n=5):
        return self.with_chairs_needed().filter(chairs_needed__lt=n)

    def with_chairs_needed(self):
        return self.annotate(chairs_needed=F('num_employees') - F('num_chairs'))

class Company(models.Model):
    # ...
    objects = CompanyQuerySet.as_manager()

Company.objects.needs_fewer_chairs_than(4).many_employees()

Xem https://docs.djangoproject.com/en/1.9/topics/db/managers/ để biết thêm. Lưu ý rằng tôi đang tắt tài liệu và chưa kiểm tra những điều trên.


14

Có vẻ như sử dụng F () với chú thích sẽ là giải pháp của tôi cho điều này.

Nó sẽ không lọc theo @property, vì Fcác cuộc nói chuyện với databse trước khi các đối tượng được đưa vào python. Nhưng vẫn đặt nó ở đây như một câu trả lời vì lý do tôi muốn lọc theo thuộc tính là thực sự muốn lọc các đối tượng bằng kết quả của phép tính đơn giản trên hai trường khác nhau.

vì vậy, một cái gì đó dọc theo dòng của:

companies = Company.objects\
    .annotate(chairs_needed=F('num_employees') - F('num_chairs'))\
    .filter(chairs_needed__lt=4)

thay vì xác định thuộc tính là:

@property
def chairs_needed(self):
    return self.num_employees - self.num_chairs

sau đó đọc toàn bộ danh sách trên tất cả các đối tượng.


5

Tôi đã gặp vấn đề tương tự và tôi đã phát triển giải pháp đơn giản này:

objects_id = [x.id for x in MyModel.objects.all() if x.myProperty == [...]]
MyModel.objects.filter(id__in=objects_id)

Tôi biết đó không phải là giải pháp hiệu quả nhất, nhưng có thể hữu ích trong những trường hợp đơn giản như của tôi


3

XIN ai đó sửa cho tôi, nhưng tôi đoán tôi đã tìm thấy một giải pháp, ít nhất là cho trường hợp của riêng tôi.

Tôi muốn làm việc trên tất cả các phần tử có thuộc tính chính xác bằng ... bất cứ thứ gì.

Nhưng tôi có một số mô hình và quy trình này sẽ hoạt động cho tất cả các mô hình. Và nó có:

def selectByProperties(modelType, specify):
    clause = "SELECT * from %s" % modelType._meta.db_table

    if len(specify) > 0:
        clause += " WHERE "
        for field, eqvalue in specify.items():
            clause += "%s = '%s' AND " % (field, eqvalue)
        clause = clause [:-5]  # remove last AND

    print clause
    return modelType.objects.raw(clause)

Với chương trình con phổ quát này, tôi có thể chọn tất cả các phần tử đó chính xác bằng từ điển của tôi về các tổ hợp 'chỉ định' (tên thuộc tính, giá trị thuộc tính).

Tham số đầu tiên nhận a (models.Model),

từ điển thứ hai như: {"property1": "77", "property2": "12"}

Và nó tạo ra một câu lệnh SQL như

SELECT * from appname_modelname WHERE property1 = '77' AND property2 = '12'

và trả về một QuerySet trên các phần tử đó.

Đây là một chức năng kiểm tra:

from myApp.models import myModel

def testSelectByProperties ():

    specify = {"property1" : "77" , "property2" : "12"}
    subset = selectByProperties(myModel, specify)

    nameField = "property0"
    ## checking if that is what I expected:
    for i in subset:
        print i.__dict__[nameField], 
        for j in specify.keys():
             print i.__dict__[j], 
        print 

Và? Bạn nghĩ sao?


Nói chung nó có vẻ như một công việc tốt xung quanh. Tôi sẽ không nói điều đó là lý tưởng, nhưng điều đó có nghĩa là bạn phải phân nhánh một kho lưu trữ để sửa đổi mô hình cho các gói bạn đã cài đặt từ PyPI mỗi khi bạn cần thứ gì đó như thế này.
hlongmore

Và bây giờ tôi đã có thời gian để chơi với nó một chút: nhược điểm thực sự của cách tiếp cận này là các tập truy vấn được trả về bởi .raw () không phải là các tập truy vấn chính thức, do đó tôi có nghĩa là có các phương thức queryset bị thiếu:AttributeError: 'RawQuerySet' object has no attribute 'values'
hlongmore

1

Tôi biết đó là một câu hỏi cũ, nhưng vì lợi ích của những người đang nhảy ở đây, tôi nghĩ sẽ hữu ích khi đọc câu hỏi bên dưới và câu trả lời tương đối:

Cách tùy chỉnh bộ lọc quản trị trong Django 1.4


1
Đối với những người đọc lướt câu trả lời này - liên kết này là thông tin về việc triển khai Bộ lọc danh sách trong Quản trị viên Django bằng cách sử dụng "SimpleListFilter". Hữu ích, nhưng không phải là câu trả lời cho câu hỏi trừ một trường hợp rất cụ thể.
jenniwren

0

Nó cũng có thể có thể sử dụng chú thích queryset đó lặp lại trong các get tài sản / thiết logic, như ví dụ được đề xuất bởi @rattray@thegrimmscientist , kết hợp với các property. Điều này có thể mang lại thứ gì đó hoạt động cả ở cấp Python cấp cơ sở dữ liệu.

Tuy nhiên, không chắc chắn về những hạn chế: hãy xem câu hỏi SO này để làm ví dụ.


Liên kết câu hỏi đánh giá mã của bạn đưa ra thông báo rằng nó đã bị tác giả của nó tự nguyện xóa. Bạn có phiền cập nhật câu trả lời của mình ở đây không, kèm theo liên kết tới mã hoặc kèm theo lời giải thích hoặc chỉ xóa câu trả lời của bạn?
hlongmore

@hlongmore: Xin lỗi vì điều đó. Câu hỏi đó đã được chuyển đến SO. Tôi đã sửa liên kết ở trên.
djvg
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.