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

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

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

Ответ 1

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

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

Person.objects.filter(**kwargs)

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

Ответ 2

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

В приложении для опроса 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")   

Ответ 3

Django.db.models.Q - это именно то, что вы хотите в способе Django.

Ответ 4

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

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

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

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

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

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