В Django, как можно фильтровать QuerySet с динамическими поисками полей?

161

Учитывая класс:

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`.
Брайан М. Хант
источник

Ответы:

311

Расширение аргумента Python может быть использовано для решения этой проблемы:

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

Person.objects.filter(**kwargs)

Это очень распространенная и полезная идиома Python.

Даниэль Нааб
источник
6
Просто быстрое понимание: убедитесь, что строки в kwargs имеют тип str, а не unicode, иначе filter () будет ворчать.
Стив Джалим
1
@santiagobasulto Это также относится к параметру упаковки / распаковки и их изменения.
Даниэль Нааб
7
хорошо, красиво и приятно !
Оскар Медерос
5
@DanielNaab, но это будет работать только для kwargs, работающих над фильтрацией условий AND, любой альтернативой для условия OR.
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")   
shacker
источник
Мне интересно, что потребуется для создания пользовательского ModelField / FormField / WidgetField, в котором реализовано поведение, позволяющее пользователю на стороне графического интерфейса в основном «строить» запрос, никогда не видя реального текста, но используя интерфейс для Сделай так. Звучит как аккуратный проект ...
Т. Стоун
1
Т. Стоун - Я полагаю, что было бы легко создать такой инструмент в упрощенном виде, если бы модели, которые требуют запросов, были простыми, но очень сложными для выполнения, полностью раскрывая все возможные варианты, особенно если модели были сложный.
шейкер
5
-1 вызов eval()при импорте пользователей - плохая идея, даже если вы полностью доверяете своим пользователям. Здесь лучше использовать поле JSON.
Джон Картер
5

Django.db.models.Q - это именно то, что вам нужно в стиле Django.

Brent81
источник
7
Не могли бы вы (или кто-то другой) привести пример использования объектов Q в использовании динамических имен полей?
Jackdbernier
3
Это то же самое, что и в ответе Дэниела Нааба . Единственное отличие состоит в том, что вы передаете аргументы в конструктор объекта Q. Q(**filters), если вы хотите динамически создавать объекты Q, вы можете поместить их в список и использовать .filter(*q_objects)или использовать побитовые операторы для объединения объектов Q.
Будет ли S
5
Этот ответ должен действительно включать пример использования Q для решения проблемы OP.
pdoherty926
-2

Действительно сложные формы поиска обычно указывают на то, что более простая модель пытается найти выход.

Как именно вы ожидаете получить значения для имени столбца и операции? Где Вы получаете значение ?'name''startswith'

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

    Сколько столбцов имеют такие фильтры? 6? 12? 18?

    • Немного? Сложный список выбора не имеет смысла. Несколько полей и несколько операторов if имеют смысл.
    • Большое количество? Ваша модель не звучит правильно. Похоже, «поле» на самом деле является ключом к строке в другой таблице, а не к столбцу.
  2. Специальные кнопки фильтра. Подожди ... Так работает админ Django. Специальные фильтры превращаются в кнопки. И тот же анализ, что и выше, применяется. Несколько фильтров имеют смысл. Большое количество фильтров обычно означает своего рода нарушение первой нормальной формы.

Много подобных полей часто означает, что должно быть больше строк и меньше полей.

С. Лотт
источник
9
С уважением, самонадеянно давать рекомендации, ничего не зная о дизайне. Чтобы «просто реализовать» это приложение, нужно было бы создать астрономические (> 200 приложений ^ 21 foos) функции для удовлетворения требований. Вы читаете цель и намерение в примере; ты не должен :)
Брайан М. Хант
2
Я встречаю много людей, которые считают, что их проблему было бы тривиально решить, если бы только вещи были (а) более общими и (б) работали так, как они себе представляли. Этот путь лежит бесконечное разочарование, потому что вещи не так, как они себе представляли. Я видел слишком много ошибок, связанных с «исправлением фреймворка».
S.Lott
2
Вещи работают как ожидалось и желательно согласно ответу Даниила. Мой вопрос был о синтаксисе, а не о дизайне. Если бы у меня было время выписать дизайн, я бы это сделал. Я уверен, что ваш вклад будет полезен, однако это просто не практичный вариант.
Брайан М. Хант
8
С. Лотт, ваш ответ даже не дает ответа на этот вопрос. Если вы не знаете ответа, оставьте вопрос в покое. Не отвечайте на нежелательные советы по дизайну, если у вас абсолютно нулевые знания о дизайне!
Слайпет
2
@slypete: Если изменение дизайна устраняет проблему, значит, проблема решена. Продолжение пути по плохому дизайну обходится дороже и сложнее, чем необходимо. Решение первопричин проблем лучше, чем решение других проблем, связанных с неправильными проектными решениями. Извините, вам не нравится анализ первопричин. Но когда что-то действительно сложно, это обычно означает, что вы пытаетесь сделать что-то не то с самого начала.
S.Lott