Учитывая класс:
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`.






Действительно сложные формы поиска обычно указывают на то, что более простая модель пытается найти выход.
Как именно вы ожидаете получить значения для имени столбца и операции?
Где взять значения 'name' и 'startswith'?
filter_by = '%s__%s' % ('name', 'startswith')
Форма "поиска"? Вы собираетесь - что? - выбрать имя из списка имен? Выбрать операцию из списка операций? Несмотря на открытый характер, большинство людей находят это запутанным и трудным в использовании.
Сколько столбцов имеют такие фильтры? 6? 12? 18?
Специальные кнопки фильтров. Подождите ... Так работает админ Django. Конкретные фильтры превращаются в кнопки. Применяется тот же анализ, что и выше. Несколько фильтров имеют смысл. Большое количество фильтров обычно означает своего рода первое нарушение нормальной формы.
Большое количество похожих полей часто означает, что должно быть больше строк и меньше полей.
Django уже является универсальным является. Писать более общие вещи поверх Django - это слишком много общего. Я рекомендую просто реализовать ваше приложение, избегая чрезмерного обобщения уже общей структуры.
С уважением, самонадеянно давать рекомендации, ничего не зная о дизайне. Чтобы «просто реализовать» это приложение, потребовались бы астрономические (> 200 приложений ^ 21 фут) функции, отвечающие требованиям. Вы читаете цель и намерение в примере; ты не должен. :)
Я встречаю множество людей, которые считают, что их проблему было бы тривиально решить, если бы все было (а) более общим и (б) работало так, как они себе представляли. Это приводит к бесконечному разочарованию, потому что все идет не так, как они себе представляли. Я видел слишком много неудач, связанных с «исправлением фреймворка».
@BMH: Если я ничего не знаю о дизайне, то, наверное, вопрос неполный. Если я не должен предполагать, как я могу получить достаточно информации? Если вам не нравится мое предположение, возможно, вы могли бы предоставить факты, чтобы показать мне, насколько я ошибаюсь.
Согласно ответу Даниэля, все работает так, как ожидалось и желательно. Мой вопрос касался синтаксиса, а не дизайна. Если бы у меня было время написать дизайн, я бы это сделал. Я уверен, что ваш вклад будет полезен, но это просто непрактичный вариант.
@BMH: вот в чем суть: пожалуйста, не называйте это самонадеянным, если вопрос может быть неполным. Вы не можете жаловаться на ответы на SO с самого начала, не говоря уже о том, что они не подходят для вопроса, который кажется необоснованным.
С.Лотт, ваш ответ даже отдаленно не отвечает на этот вопрос. Если вы не знаете ответа, оставьте вопрос в покое. Не отвечайте незапрашиваемыми советами по дизайну, если вы абсолютно ничего не знаете о дизайне!
@slypete: Если изменение дизайна устраняет проблему, значит, проблема решена. Продолжение пути, основанного на плохом дизайне, дороже и сложнее, чем необходимо. Лучше решать первопричину проблем, чем решать другие проблемы, возникающие из-за неверных проектных решений. Извините, вам не нравится анализ первопричин. Но когда что-то действительно сложно, это обычно означает, что вы с самого начала пытаетесь сделать не то.
Для решения этой проблемы можно использовать расширение аргументов Python:
kwargs = {
'{0}__{1}'.format('name', 'startswith'): 'A',
'{0}__{1}'.format('name', 'endswith'): 'Z'
}
Person.objects.filter(**kwargs)
Это очень распространенная и полезная идиома Python.
Просто небольшая хитрость: убедитесь, что строки в kwargs имеют тип str, а не unicode, иначе filter () будет ворчать.
Спасибо, Даниэль! Мне это помогло. Как это называется в Python? Расширение аргументов? Я не нашел его в документации.
@santiagobasulto Это также относится к упаковке / распаковке параметров и их вариациям.
отличное решение! сенсация!
@DanielNaab, но это будет работать только с kwargs, работающим с фильтрацией условия И, любой альтернативой для условия ИЛИ.
@prateek вы всегда можете использовать объекты Q: stackoverflow.com/questions/13076822/…
@deecodameeko, как Q-объекты внутри kwargs?
@deecodameeko Вам не нужны объекты Q внутри kwargs, вам просто нужен обратный подход: Q (** kwargs1) | Q (** kwargs2) и т. д. Таким образом, вы можете комбинировать столько динамических условий «ИЛИ», сколько вам нужно.
Вы можете свернуть это дальше в: Person.objects.filter (** {'{0} __ {1}'. Format ('name', 'startwith'): 'A', '{0} __ {1}' .format ('имя', 'заканчивается на'): 'Z'})
Что ж, без обид, но это похоже на взлом для достижения чего-то, что кажется естественно возможным. Разве не должно быть способа выполнить поиск на основе имени динамического поля, предоставленного напрямую Django (без использования raw sql)
Обязательно ли использовать промежуточную переменную (kwargs) или вы можете просто указать словарь критериев фильтрации непосредственно в качестве аргумента функции filter ()? И если да, то разве это должно называться «кваргс»?
@ JoeMjr2 kwargs может быть любым именем переменной, и вы также можете сделать это встроенным: Person.objects.filter(**{'{0}__{1}'.format('name', 'startswith'): 'A'})
Упрощенный пример:
В приложении для опроса 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, который реализует поведение, позволяющее пользователю на стороне графического интерфейса в основном «строить» запрос, никогда не видя фактического текста, но используя интерфейс для Сделай так. Звучит как отличный проект ...
Т. Стоун: Я бы предположил, что было бы легко создать такой инструмент в упрощенном виде, если бы модели, требующие запросов, были простыми, но их было бы очень трудно сделать тщательно, раскрывая все возможные варианты, особенно если бы модели сложный.
-1 вызывать eval() при импорте пользователей - плохая идея, даже если вы полностью доверяете своим пользователям. Поле JSON здесь было бы лучше.
Django.db.models.Q - это именно то, что вам нужно в Django.
Не могли бы вы (или кто-то) привести пример того, как использовать объекты Q при использовании динамических имен полей?
Это то же самое, что и в Ответ Даниэля Нааба, с той лишь разницей, что вы передаете аргументы в конструктор объекта Q. Q(**filters), если вы хотите динамически создавать объекты Q, вы можете поместить их в список и использовать .filter(*q_objects) или использовать побитовые операторы для объединения объектов Q.
Этот ответ действительно должен включать пример использования Q для решения проблемы OP.
Спасибо за ответ. Модель данных, приведенная в качестве примера, предназначена для упрощения иллюстрации. Я предпочитаю не представлять, как кто-то вкладывает что-то настолько отвратительное в настоящий проект. ;) Я хочу отделить общие отношения и исключить некоторую повторно используемую логику.