Учитывая класс:
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'.
Для решения этой проблемы можно использовать расширение аргумента Python:
kwargs = {
'{0}__{1}'.format('name', 'startswith'): 'A',
'{0}__{1}'.format('name', 'endswith'): 'Z'
}
Person.objects.filter(**kwargs)
Это очень распространенная и полезная идиома Python.
Упрощенный пример:
В приложении для опроса 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")
Django.db.models.Q - это именно то, что вы хотите в способе Django.
Q(**filters)
, если вы хотите динамически создавать объекты Q, вы можете поместить их в список и использовать .filter(*q_objects)
или использовать побитовые операторы для объединения объектов Q.
Действительно сложные формы поиска обычно указывают на то, что более простая модель пытается выкопать ее.
Как именно вы ожидаете получить значения для имени столбца и операции?
Где вы получаете значения 'name'
a 'startswith'
?
filter_by = '%s__%s' % ('name', 'startswith')
Форма "поиска"? Ты собираешься - что? - выбрать имя из списка имен? Выберите операцию из списка операций? Хотя люди с открытым исходным кодом, большинство людей находят это запутанным и трудным в использовании.
Сколько столбцов имеют такие фильтры? 6? 12? 18?
Специальные кнопки фильтра. Подождите... Так работает администратор Django. Конкретные фильтры превращаются в кнопки. И применяется тот же анализ, что и выше. Несколько фильтров имеют смысл. Большое количество фильтров обычно означает своеобразное нарушение первой нормальной формы.
Много похожих полей часто означает, что должно быть больше строк и меньше полей.