Ответ 1
Неа. Фильтры Django работают на уровне базы данных, генерируя SQL. Чтобы фильтровать на основе свойств Python, вы должны загрузить объект в Python для оценки свойства - и в этот момент вы уже выполнили всю работу по его загрузке.
Можно ли отфильтровать набор запросов Django по свойству модели?
у меня есть метод в моей модели:
@property
def myproperty(self):
[..]
и теперь я хочу фильтровать по этому свойству, как:
MyModel.objects.filter(myproperty=[..])
это как-то возможно?
Неа. Фильтры Django работают на уровне базы данных, генерируя SQL. Чтобы фильтровать на основе свойств Python, вы должны загрузить объект в Python для оценки свойства - и в этот момент вы уже выполнили всю работу по его загрузке.
Возможно, я неправильно понял ваш оригинальный вопрос, но есть 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())
Похоже, с помощью 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
затем выполнив проверку списка по всем объектам.
Обойдя @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/ для получения дополнительной информации. Обратите внимание, что я ухожу от документации и не проверял выше.
ПОЖАЛУЙСТА, меня кто-то исправит, но я думаю, что нашел решение, по крайней мере, для моего собственного дела.
Я хочу работать над всеми теми элементами, свойства которых в точности равны... независимо.
Но у меня есть несколько моделей, и эта процедура должна работать для всех моделей. И это делает:
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
А? Как вы думаете?
Я знаю, что это старый вопрос, но ради тех, кто прыгает здесь, я думаю, что полезно прочитать вопрос ниже и относительный ответ:
Также возможно использовать аннотации наборов запросов, которые дублируют свойство get/set-logic, как это предлагается, например, @rattray и @thegrimmscientist, вместе со property
. Это может привести к тому, что работает как на уровне Python, так и на уровне базы данных.
Не уверен насчет недостатков, однако: см. Мой вопрос CodeReview для примера.