В Django, как можно фильтровать QuerySet с динамическими поисками полей? - PullRequest
141 голосов
/ 22 ноября 2008

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

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`.

Ответы [ 4 ]

272 голосов
/ 22 ноября 2008

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

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

Person.objects.filter(**kwargs)

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

6 голосов
/ 18 марта 2009

Упрощенный пример:

В приложении-опросе 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")   
5 голосов
/ 23 мая 2013

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

0 голосов
/ 22 ноября 2008

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

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

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

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

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

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

...