Подтвердить что ты не робот

Фильтровать по собственности

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

Возможно, я неправильно понял ваш оригинальный вопрос, но есть filter, встроенный в 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())

Ответ 3

Похоже, с помощью 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

затем выполнив проверку списка по всем объектам.

Ответ 4

Обойдя @TheGrimmScientist предложенный обходной путь, вы можете сделать эти "свойства 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/ для получения дополнительной информации. Обратите внимание, что я ухожу от документации и не проверял выше.

Ответ 5

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

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

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

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 

А? Как вы думаете?

Ответ 7

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

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