Как в 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`.
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
169
0
61 670
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

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

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

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

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

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

Спасибо за ответ. Модель данных, приведенная в качестве примера, предназначена для упрощения иллюстрации. Я предпочитаю не представлять, как кто-то вкладывает что-то настолько отвратительное в настоящий проект. ;) Я хочу отделить общие отношения и исключить некоторую повторно используемую логику.

Brian M. Hunt 22.11.2008 08:52

Django уже является универсальным является. Писать более общие вещи поверх Django - это слишком много общего. Я рекомендую просто реализовать ваше приложение, избегая чрезмерного обобщения уже общей структуры.

S.Lott 22.11.2008 16:36

С уважением, самонадеянно давать рекомендации, ничего не зная о дизайне. Чтобы «просто реализовать» это приложение, потребовались бы астрономические (> 200 приложений ^ 21 фут) функции, отвечающие требованиям. Вы читаете цель и намерение в примере; ты не должен. :)

Brian M. Hunt 22.11.2008 18:07

Я встречаю множество людей, которые считают, что их проблему было бы тривиально решить, если бы все было (а) более общим и (б) работало так, как они себе представляли. Это приводит к бесконечному разочарованию, потому что все идет не так, как они себе представляли. Я видел слишком много неудач, связанных с «исправлением фреймворка».

S.Lott 22.11.2008 18:36

@BMH: Если я ничего не знаю о дизайне, то, наверное, вопрос неполный. Если я не должен предполагать, как я могу получить достаточно информации? Если вам не нравится мое предположение, возможно, вы могли бы предоставить факты, чтобы показать мне, насколько я ошибаюсь.

S.Lott 22.11.2008 18:38

Согласно ответу Даниэля, все работает так, как ожидалось и желательно. Мой вопрос касался синтаксиса, а не дизайна. Если бы у меня было время написать дизайн, я бы это сделал. Я уверен, что ваш вклад будет полезен, но это просто непрактичный вариант.

Brian M. Hunt 22.11.2008 19:29

@BMH: вот в чем суть: пожалуйста, не называйте это самонадеянным, если вопрос может быть неполным. Вы не можете жаловаться на ответы на SO с самого начала, не говоря уже о том, что они не подходят для вопроса, который кажется необоснованным.

S.Lott 22.11.2008 23:05

С.Лотт, ваш ответ даже отдаленно не отвечает на этот вопрос. Если вы не знаете ответа, оставьте вопрос в покое. Не отвечайте незапрашиваемыми советами по дизайну, если вы абсолютно ничего не знаете о дизайне!

slypete 18.08.2009 10:28

@slypete: Если изменение дизайна устраняет проблему, значит, проблема решена. Продолжение пути, основанного на плохом дизайне, дороже и сложнее, чем необходимо. Лучше решать первопричину проблем, чем решать другие проблемы, возникающие из-за неверных проектных решений. Извините, вам не нравится анализ первопричин. Но когда что-то действительно сложно, это обычно означает, что вы с самого начала пытаетесь сделать не то.

S.Lott 18.08.2009 14:11
Ответ принят как подходящий

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

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

Person.objects.filter(**kwargs)

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

Просто небольшая хитрость: убедитесь, что строки в kwargs имеют тип str, а не unicode, иначе filter () будет ворчать.

Steve Jalim 04.04.2011 13:30

Спасибо, Даниэль! Мне это помогло. Как это называется в Python? Расширение аргументов? Я не нашел его в документации.

santiagobasulto 10.12.2011 11:18

@santiagobasulto Это также относится к упаковке / распаковке параметров и их вариациям.

Daniel Naab 10.12.2011 21:25

отличное решение! сенсация!

anand 04.01.2013 15:19

@DanielNaab, но это будет работать только с kwargs, работающим с фильтрацией условия И, любой альтернативой для условия ИЛИ.

Prateek099 16.08.2016 15:55

@prateek вы всегда можете использовать объекты Q: stackoverflow.com/questions/13076822/…

deecodameeko 25.02.2017 00:50

@deecodameeko, как Q-объекты внутри kwargs?

Kelvin Barsana 01.03.2017 06:15

@deecodameeko Вам не нужны объекты Q внутри kwargs, вам просто нужен обратный подход: Q (** kwargs1) | Q (** kwargs2) и т. д. Таким образом, вы можете комбинировать столько динамических условий «ИЛИ», сколько вам нужно.

abcdn 19.09.2017 01:24

Вы можете свернуть это дальше в: Person.objects.filter (** {'{0} __ {1}'. Format ('name', 'startwith'): 'A', '{0} __ {1}' .format ('имя', 'заканчивается на'): 'Z'})

Paul Kenjora 29.07.2018 21:43

Что ж, без обид, но это похоже на взлом для достижения чего-то, что кажется естественно возможным. Разве не должно быть способа выполнить поиск на основе имени динамического поля, предоставленного напрямую Django (без использования raw sql)

user3245268 27.02.2019 19:19

Обязательно ли использовать промежуточную переменную (kwargs) или вы можете просто указать словарь критериев фильтрации непосредственно в качестве аргумента функции filter ()? И если да, то разве это должно называться «кваргс»?

JoeMjr2 16.01.2020 00:26

@ JoeMjr2 kwargs может быть любым именем переменной, и вы также можете сделать это встроенным: Person.objects.filter(**{'{0}__{1}'.format('name', 'startswith'): 'A'})

Daniel Naab 17.01.2020 01:04

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

В приложении для опроса 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, который реализует поведение, позволяющее пользователю на стороне графического интерфейса в основном «строить» запрос, никогда не видя фактического текста, но используя интерфейс для Сделай так. Звучит как отличный проект ...

T. Stone 24.09.2009 00:23

Т. Стоун: Я бы предположил, что было бы легко создать такой инструмент в упрощенном виде, если бы модели, требующие запросов, были простыми, но их было бы очень трудно сделать тщательно, раскрывая все возможные варианты, особенно если бы модели сложный.

shacker 28.09.2009 10:59

-1 вызывать eval() при импорте пользователей - плохая идея, даже если вы полностью доверяете своим пользователям. Поле JSON здесь было бы лучше.

John Carter 20.09.2017 05:59

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

Не могли бы вы (или кто-то) привести пример того, как использовать объекты Q при использовании динамических имен полей?

jackdbernier 27.08.2014 03:32

Это то же самое, что и в Ответ Даниэля Нааба, с той лишь разницей, что вы передаете аргументы в конструктор объекта Q. Q(**filters), если вы хотите динамически создавать объекты Q, вы можете поместить их в список и использовать .filter(*q_objects) или использовать побитовые операторы для объединения объектов Q.

Will S 02.03.2016 18:58

Этот ответ действительно должен включать пример использования Q для решения проблемы OP.

pdoherty926 05.03.2019 18:55

Другие вопросы по теме