Фільтрувати за властивістю


95

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

у мене є метод у своїй моделі:

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

і тепер я хочу відфільтрувати за цією властивістю, як:

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

це якось можливо?


Це в SQLAlchemy: docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html, і ви можете підключити django до SQLAlchemy через pypi.python.org/pypi/aldjemy, але я сумніваюся, що ці два можуть бути пов’язані такими, якими ви хочете, щоб вони були.
rattray

Відповіді:


78

Ні. Фільтри Django працюють на рівні бази даних, генеруючи SQL. Щоб фільтрувати на основі властивостей Python, вам потрібно завантажити об’єкт у Python, щоб оцінити властивість - і на той момент ви вже зробили всю роботу, щоб завантажити його.


5
Невдача в тому, що ця функція не реалізована, стане цікавим розширенням для принаймні фільтрації відповідних об’єктів після побудови набору результатів.
шнек

1
як з цим боротися в адмінку? Чи є якесь обхідне рішення?
andilabs

39

Можливо, я неправильно розумію ваше початкове запитання, але в 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())

15
Це працює для його фільтрації, коли у вас є об’єкт Python, але він запитує про Django QuerySet.filter, який створює SQL-запити.
Гленн Мейнард,

1
правильно, але, як пояснено вище, я хотів би додати властивість до мого фільтра бази даних. фільтрація після завершення запиту - саме те, чого я хочу уникати.
schneck

19

Виконуючи запропонований обхідний шлях @ TheGrimmScientist, ви можете створити ці "властивості sql", визначивши їх у диспетчері або QuerySet, і повторно використати / скласти / скласти їх:

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

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)

За допомогою набору запитів:

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/ . Зауважте, що я відмовляюся від документації та не перевіряв вищезазначене.


14

Схоже, використання F () з анотаціями буде моїм рішенням для цього.

Це не збирається фільтрувати @property, оскільки Fрозмовляє з databse перед тим, як об’єкти вводяться в 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

потім робимо розуміння списку для всіх об’єктів.


5

У мене була та ж проблема, і я розробив це просте рішення:

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

Я знаю, що це не найефективніше рішення, але може допомогти у простих випадках, як у мене


3

Будь ласка, хтось виправте мене, але, мабуть, я знайшов рішення, принаймні для власного випадку.

Я хочу працювати над усіма тими елементами, властивості яких точно дорівнюють ... як завгодно.

Але у мене є кілька моделей, і ця процедура повинна працювати для всіх моделей. І це робить:

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)

За допомогою цієї універсальної підпрограми я можу вибрати всі ті елементи, які точно відповідають моєму словнику комбінацій «задати» (ім’я властивості, значення властивості).

Перший параметр приймає (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 

І? Що ти думаєш?


Як правило, це здається гідною роботою. Я б не сказав, що це ідеально, але це необхідність розгалужувати сховище, щоб модифікувати модель для пакетів, які ви встановили з PyPI кожного разу, коли вам потрібно щось подібне.
hlongmore

І тепер, коли я встиг трохи пограти з цим: справжнім недоліком цього підходу є те, що набори запитів, повернені .raw (), не є повноцінними наборами запитів, під якими я маю на увазі, що відсутні методи набору запитів, які відсутні:AttributeError: 'RawQuerySet' object has no attribute 'values'
hlongmore

1

я знаю, що це старе запитання, але заради тих, хто сюди стрибає, я думаю, що корисно прочитати питання нижче та відносну відповідь:

Як налаштувати фільтр адміністратора в Django 1.4


1
Для тих, хто обробляє цю відповідь - це посилання на інформацію про впровадження фільтрів списку в адміністраторі Django за допомогою "SimpleListFilter". Корисна, але не відповідь на запитання, за винятком дуже конкретного випадку.
jenniwren

0

Він також може бути можливим використовувати QuerySet анотації , які дублюють властивість Get / Set-логіку, як це було запропоновано , наприклад , з допомогою @rattray і @thegrimmscientist , в поєднанні зproperty . Це може дати щось, що працює як на рівні Python, так і на рівні бази даних.

Не впевнені у недоліках, однак: див. Це запитання SO для прикладу.


Посилання на ваше запитання про перевірку коду повідомляє, що його добровільно було видалено його автором. Не могли б Ви оновити свою відповідь тут, або з посиланням на код, або з поясненням, або просто видалити свою відповідь?
hlongmore

@hlongmore: Вибачте за це. Це питання було перенесено до SO. Я зафіксував посилання вище.
djvg
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.