Фильтровать по свойству - PullRequest
73 голосов
/ 30 июля 2009

Можно ли отфильтровать набор запросов Django по свойству модели?

У меня есть метод в моей модели:

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

и теперь я хочу фильтровать по этому свойству как:

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

это как-то возможно?

Ответы [ 7 ]

65 голосов
/ 30 июля 2009

Неа. Фильтры Django работают на уровне базы данных, генерируя SQL. Чтобы выполнить фильтрацию на основе свойств Python, необходимо загрузить объект в Python для оценки свойства - и в этот момент вы уже проделали всю работу по его загрузке.

34 голосов
/ 30 июля 2009

Возможно, я неправильно понял ваш исходный вопрос, но в Python встроен фильтр .

filtered = filter(myproperty, MyModel.objects)

Но лучше использовать список понимания :

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

или даже лучше, выражение генератора :

filtered = (x for x in MyModel.objects if x.myproperty())
12 голосов
/ 09 февраля 2016

Похоже, использование F () с аннотациями будет моим решением для этого.

Он не собирается фильтровать по @property, поскольку F обращается к базе данных до того, как объекты будут введены в python. Но все-таки я поставил его здесь как ответ, поскольку моя причина, по которой мне нужно было фильтровать по свойству, действительно хотела отфильтровать объекты по результатам простой арифметики в двух разных полях.

Итак, что-то вроде:

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

вместо определения свойства следующим образом:

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

затем выполняет понимание списка для всех объектов.

11 голосов
/ 03 мая 2016

Обойдя предложенный обходной путь @GrimmScientist, вы можете создать эти "свойства sql", определив их в Manager или QuerySet, и повторно использовать / chain / compose их:

С менеджером:

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)

с помощью 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()

Подробнее см. https://docs.djangoproject.com/en/1.9/topics/db/managers/. Обратите внимание, что я ухожу от документации и не проверял выше.

3 голосов
/ 26 ноября 2014

ПОЖАЛУЙСТА, кто-нибудь поправит меня, но я думаю, что нашел решение, по крайней мере, для моего собственного случая.

Я хочу работать со всеми теми элементами, чьи свойства в точности равны ... чему угодно.

Но у меня есть несколько моделей, и эта процедура должна работать для всех моделей. И это делает:

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)

С помощью этой универсальной подпрограммы я могу выбрать все те элементы, которые в точности соответствуют моему словарю комбинаций «указать» (propertyname, propertyvalue).

Первый параметр принимает (models.Model),

второй словарь вроде: {"property1": "77", "property2": "12"}

И он создает инструкцию SQL, такую ​​как

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

и возвращает QuerySet для этих элементов.

Это тестовая функция:

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 

А? Что ты думаешь?

1 голос
/ 12 октября 2013

я знаю, что это старый вопрос, но для тех, кто прыгает здесь, я думаю, что было бы полезно прочитать вопрос ниже и соответствующий ответ:

Как настроить фильтр администратора в Django 1.4

0 голосов
/ 10 декабря 2018

Также возможно использовать аннотации наборов запросов, которые дублируют свойство get / set-logic, как это предлагается, например, @ rattray и @ thegrimmscientist , в сочетании с property.Это может привести к тому, что работает как на уровне Python , так и на уровне базы данных.

Не уверен насчет недостатков, однако: см. этот вопрос SO для примера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...