Як у Django, як фільтрувати QuerySet з динамічними пошуками полів?


160

Дано клас:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=20)

Чи можливо, і якщо так, то мати QuerySet, який фільтрує на основі динамічних аргументів? Наприклад:

 # Instead of:
 Person.objects.filter(name__startswith='B')
 # ... and:
 Person.objects.filter(name__endswith='B')

 # ... is there some way, given:
 filter_by = '{0}__{1}'.format('name', 'startswith')
 filter_value = 'B'

 # ... that you can run the equivalent of this?
 Person.objects.filter(filter_by=filter_value)
 # ... which will throw an exception, since `filter_by` is not
 # an attribute of `Person`.

Відповіді:


310

Розв’язання аргументів Python може використовуватися для вирішення цієї проблеми:

kwargs = {
    '{0}__{1}'.format('name', 'startswith'): 'A',
    '{0}__{1}'.format('name', 'endswith'): 'Z'
}

Person.objects.filter(**kwargs)

Це дуже поширена і корисна ідіома Python.


6
Просто швидкий хедш-ап: переконайтесь, що рядки в кворгах типу str не є unicode, інакше filter () буде бурчати.
Стів Ялім

1
@santiagobasulto Також називається упаковкою / розпакуванням параметрів та їх варіантами.
Даніель Нааб

7
приємно, приємно і приємно !
Оскар Медерос

5
@DanielNaab, але це буде працювати тільки на kwargs, що працюють на фільтрації AND AND, будь-яку альтернативу для умови АБО.
Prateek099

3
@prateek ви завжди можете використовувати Q об'єкти: stackoverflow.com/questions/13076822 / ...
deecodameeko

6

Спрощений приклад:

У додатку для опитування Django я хотів вибрати список вибору HTML, де відображаються зареєстровані користувачі. Але оскільки у нас є 5000 зареєстрованих користувачів, мені знадобився спосіб фільтрації цього списку на основі критеріїв запитів (наприклад, лише людей, які пройшли певний семінар). Для того, щоб елемент опитування міг бути використаний повторно, мені потрібно, щоб особа, яка створює питання опитування, змогла приєднати ці критерії до цього питання (не хочу жорстко кодувати запит у програмі).

Я запропонував рішення не на 100% зручне в користуванні (для створення запиту потрібна допомога технічної людини), але це вирішує проблему. Створюючи запитання, редактор може ввести словник у спеціальне поле, наприклад:

{'is_staff':True,'last_name__startswith':'A',}

Ця рядок зберігається в базі даних. У коді перегляду він повертається як self.question.custom_query. Значення цього є рядком , яка виглядає як словник. Ми повертаємо його до справжнього словника з eval (), а потім вводимо його у набір запитів із ** kwargs:

kwargs = eval(self.question.custom_query)
user_list = User.objects.filter(**kwargs).order_by("last_name")   

Мені цікаво, що знадобиться для створення користувальницького ModelField / FormField / WidgetField, який реалізував поведінку, щоб дозволити користувачеві з боку GUI в основному "будувати" запит, ніколи не бачачи фактичного тексту, але використовуючи інтерфейс для зроби так. Звучить як акуратний проект ...
Т. Стоун,

1
Т. Стоун - Я думаю, було б легко створити такий інструмент спрощеним способом, якби моделі, які потребують запиту, були простими, але дуже важко зробити ретельним способом, який викривав усі можливі варіанти, особливо якщо моделі були складний.
шейкер

5
-1 заклик eval()до імпорту користувача - погана ідея, навіть якщо ви повністю довіряєте своїм користувачам. Тут буде кращою ідеєю поле JSON.
Джон Картер

5

Django.db.models.Q - це саме те, що ви хочете в режимі Django.


7
Чи можете ви (чи хтось) надати приклад того, як використовувати Q-об’єкти при використанні імен динамічних полів?
jackdbernier

3
Це те саме, що у відповіді Даніеля Нааба . Різниця полягає лише в тому, що ви передаєте аргументи в конструктор об'єктів Q. Q(**filters), якщо ви хотіли динамічно нарощувати Q-об’єкти, ви можете помістити їх у список та використовувати .filter(*q_objects)або використовувати побітові оператори для об'єднання Q-об'єктів.
Буде S

5
Ця відповідь дійсно повинна містити приклад використання Q для вирішення проблеми ОП.
pdoherty926

-2

Дійсно складні форми пошуку зазвичай вказують на те, що простіша модель намагається викопати вихід.

Як саме ви очікуєте отримати значення назви стовпця та операції? Де Ви отримуєте значення ?'name''startswith'

 filter_by = '%s__%s' % ('name', 'startswith')
  1. Форма "пошуку"? Ви збираєтесь - що? - вибрати назву зі списку імен? Виберіть операцію зі списку операцій? Хоча відкриті, більшість людей вважають це заплутаним і важким у використанні.

    Скільки стовпців мають такі фільтри? 6? 12? 18?

    • Декілька? Складний перелік не має сенсу. Кілька полів і кілька if-висловлювань мають сенс.
    • Велика кількість? Ваша модель звучить неправильно. Здається, що "поле" - це насправді ключ до рядка в іншій таблиці, а не стовпець.
  2. Конкретні кнопки фільтра. Зачекайте ... Ось так працює адміністратор Джанго. Конкретні фільтри перетворюються на кнопки. І застосовується той же аналіз, що і вище. Кілька фільтрів мають сенс. Велика кількість фільтрів зазвичай означає своєрідне перше порушення нормальної форми.

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


9
Щодо поваги, то самовдоволено давати рекомендації, нічого не знаючи про дизайн. Щоб "просто реалізувати" цю програму, було б розроблено астрономічні (> 200 додатків ^ 21 футів) функції для задоволення вимог. Ви читаєте мету та намір у прикладі; ти не повинен. :)
Брайан М. Хант

2
Я зустрічаю багато людей, які вважають, що їх проблему було б тривіально вирішити, якби тільки речі були (а) більш загальними та (б) працювали так, як вони уявляли. Таким чином лежить нескінченна розчарування, бо все не так, як вони уявляли. Я бачив занадто багато відмов, пов'язаних із "виправленням рамки".
S.Lott

2
На думку Даніеля, справи працюють так, як і очікувалося. Моє запитання стосувалося синтаксису, а не дизайну. Якби я встиг виписати дизайн, я би це зробив. Я впевнений, що ваш внесок буде корисним, проте це просто не практичний варіант.
Брайан М. Хант

8
С.Лотт, ваша відповідь навіть віддалено не відповідає на це запитання. Якщо ви не знаєте відповіді, залиште питання в спокої. Не відповідайте на непотрібні поради щодо дизайну, коли ви абсолютно нульові знання про дизайн!
slypete

2
@slypete: якщо зміна дизайну усуває проблему, тоді проблема вирішена. Продовжувати шлях, заснований на поганій конструкції, коштує дорожче і складніше, ніж потрібно. Вирішення першопричинних проблем краще, ніж вирішення інших проблем, які випливають із поганих дизайнерських рішень. Вибачте, що вам не подобається аналіз першопричини. Але коли щось по-справжньому важко, це зазвичай означає, що ти намагаєшся почати неправильно.
С.Лотт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.