Django: запрос фильтра на основе пользовательской функции

У меня есть функция, встроенная в мой класс модели Django, и я хочу использовать эту функцию для фильтрации результатов моего запроса.

  class service:
       ......
       def is_active(self):
            if datetime.now() > self.end_time:
                  return False
            return True

Теперь я хочу использовать эту функцию в своем фильтре запросов, например

nserv = service.objects.filter(is_active=True)

Я знаю, что для этого простого случая 'is_active' я могу сделать это сравнение напрямую в запросе фильтра, но для более сложных ситуаций это может оказаться невозможным. Как мне сделать запрос на основе пользовательских функций?


person Neo    schedule 16.04.2011    source источник
comment
Кстати, вы могли бы сделать return datetime.now() <= self.end_time :-)   -  person Rikki    schedule 30.10.2014
comment
У меня была точно такая же проблема! Даже функция называлась одинаково   -  person Adrian Grzywaczewski    schedule 04.03.2018
comment
Предлагаемые здесь ответы сначала выполняют запрос, а затем фильтруют его. Для огромных наборов данных и ограничительной фильтрации было бы более эффективно хранить выходные данные функции в поле модели, чтобы выполнять фильтрацию непосредственно в запросе к базе данных, а не после.   -  person Hugo Trentesaux    schedule 01.11.2018


Ответы (4)


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

nserv = service.objects.are_active()

Этого можно добиться с помощью чего-то вроде:

class ServiceManager(models.Manager):
    def are_active(self):
        # use your method to filter results
        return you_custom_queryset

См. раздел специальные менеджеры.

person Lapin-Blanc    schedule 16.04.2011
comment
# use your method to filter results - это в значительной степени вопрос, как это сделать. - person Ignacio Vazquez-Abrams; 16.04.2011
comment
@Ignacio - На самом деле это решение мне тоже подходит. Так что я пойду с ним только ради того, чтобы попробовать что-то другое. - person Neo; 16.04.2011

У меня просто была аналогичная проблема. Проблема заключалась в том, что мне пришлось вернуть экземпляр QuerySet. Быстрое решение для меня состояло в том, чтобы сделать что-то вроде этого:

active_serv_ids = [service.id for service in Service.objects.all() if service.is_active()]
nserv = Service.objects.filter(id__in=active_serv_ids)

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

Более подробный способ сделать это:

active_serv_ids = []

for service in Service.objects.all():
    if service.is_active():
        active_serv_ids.append(service.id)

nserv = Service.objects.filter(id__in=active_serv_ids)
person Walter Renner    schedule 05.02.2015
comment
Спасибо, это идеально. В django еще нет возможности загружать результаты и выполнять фильтрацию на стороне клиента, так что это единственный способ. - person beiller; 25.10.2016
comment
это не оптимально, так как он выполняет запрос и использует только его часть, но если вы уверены, что ваш фильтр сохранит большую часть результатов, это хороший обходной путь. - person Hugo Trentesaux; 01.11.2018
comment
Работает как часы. Попробуйте django отфильтровать как можно больше перед циклом - person ineedme; 03.07.2020

Возможно, вы не сможете этого сделать, вместо этого вы можете выполнить постобработку набора запросов с помощью понимания списка. или выражение генератора.

Например:

[x for x in Q if x.somecond()]
person Ignacio Vazquez-Abrams    schedule 16.04.2011
comment
comment
@A lee - :P Я до сих пор не освоился в жаргоне Python. - person Neo; 16.04.2011
comment
@ Нео, все в порядке, я никогда раньше не слышал, чтобы люди называли их LC или Genex. - person ; 08.08.2014
comment
Это не возвращает набор запросов, поэтому его нельзя использовать в некоторых контекстах (например, для заполнения поля выбора в форме). - person blueFast; 09.02.2016

Ответ Игнасио интересен, но он не возвращает набор запросов. Это делает:

def users_by_role(role):
    users = User.objects.all()
    ids = [user.id for user in users if user.role == role]
    return users.filter(id__in=ids)
person blueFast    schedule 09.02.2016