Django Tastypie Advanced Filtering: как выполнять сложные поиски с объектами Q

У меня есть базовая модель Django, например:

<code>class Business(models.Model):
    name = models.CharField(max_length=200, unique=True)
    email = models.EmailField()
    phone = models.CharField(max_length=40, blank=True, null=True)
    description = models.TextField(max_length=500)
</code>

Мне нужно выполнить сложный запрос на приведенной выше модели, как:

<code>qset = (
    Q(name__icontains=query) |
    Q(description__icontains=query) |
    Q(email__icontains=query)
    )
results = Business.objects.filter(qset).distinct()
</code>

Я пробовал следующее с помощьюtastypie без удачи

<code>def build_filters(self, filters=None):
    if filters is None:
        filters = {}
    orm_filters = super(BusinessResource, self).build_filters(filters)

    if('query' in filters):
        query = filters['query']
        print query
        qset = (
                Q(name__icontains=query) |
                Q(description__icontains=query) |
                Q(email__icontains=query)
                )
        results = Business.objects.filter(qset).distinct()
        orm_filters = {'query__icontains': results}

    return orm_filters
</code>

а в классе Мета для вкусного пирога у меня фильтрация установлена как:

<code>filtering = {
        'name: ALL,
        'description': ALL,
        'email': ALL,
        'query': ['icontains',],
    }
</code>

Любые идеи о том, как я могу решить это?

Спасибо  - ньютон

Ответы на вопрос(3)

Я решил эту проблему так:

Class MyResource(ModelResource):

  def __init__(self, *args, **kwargs):
    super(MyResource, self).__init__(*args, **kwargs)
    self.q_filters = []

  def build_filters(self, filters=None):
    orm_filters = super(MyResource, self).build_filters(filters)

    q_filter_needed_1 = []
    if "what_im_sending_from_client" in filters:
      if filters["what_im_sending_from_client"] == "my-constraint":
        q_filter_needed_1.append("something to filter")

    if q_filter_needed_1:
      a_new_q_object = Q()
      for item in q_filter_needed:
        a_new_q_object = a_new_q_object & Q(filtering_DB_field__icontains=item)
      self.q_filters.append(a_new_q_object)

  def apply_filters(self, request, applicable_filters):
    filtered = super(MyResource, self).apply_filters(request, applicable_filters)

    if self.q_filters:
      for qf in self.q_filters:
        filtered = filtered.filter(qf)
      self.q_filters = []

    return filtered

Этот метод ощущается как более четкое разделение интересов, чем другие, которые я видел.

 04 дек. 2017 г., 23:28
Это действительно плохая идея поместить информацию, специфичную для запроса, в экземпляр ресурса. Такself.q_filters.append(a_new_q_object), Это связано с тем, что в развернутой среде с несколькими потоками у вас может получиться, что состояние одного запроса влияет на другое. Так, например, все фильтры, созданные в одном запросе, могут фактически применяться к совершенно другому, в зависимости от времени. Смотрите документы здесь:django-tastypie.readthedocs.io/en/latest/… Это проблема, что прохождениеbundle Объект вокруг везде решает.

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

Основное отличие состоит в том, что apply_filters сделан более надежным благодаря использованиюNone в качестве ключа вместоcustom (который может конфликтовать с именем столбца).

def build_filters(self, filters=None):
    if filters is None:
        filters = {}
    orm_filters = super(BusinessResource, self).build_filters(filters)

    if 'query' in filters:
        query = filters['query']
        qset = (
                Q(name__icontains=query) |
                Q(description__icontains=query) |
                Q(email__icontains=query)
                )
        orm_filters.update({None: qset}) # None is used as the key to specify that these are non-keyword filters

    return orm_filters

def apply_filters(self, request, applicable_filters):
    return self.get_object_list(request).filter(*applicable_filters.pop(None, []), **applicable_filters)
    # Taking the non-keyword filters out of applicable_filters (if any) and applying them as positional arguments to filter()
Решение Вопроса

Ты на правильном пути. Тем не мение,build_filters предполагается преобразовать поиск ресурсов в поиск ORM.

Реализация по умолчанию разделяет ключевое слово запроса на основе__ в key_bits, пары значений, а затем пытается найти соответствие между искомым ресурсом и его эквивалентом ORM.

Ваш код не должен применять фильтр, только построить его. Вот улучшенная и исправленная версия:

def build_filters(self, filters=None):
    if filters is None:
        filters = {}
    orm_filters = super(BusinessResource, self).build_filters(filters)

    if('query' in filters):
        query = filters['query']
        qset = (
                Q(name__icontains=query) |
                Q(description__icontains=query) |
                Q(email__icontains=query)
                )
        orm_filters.update({'custom': qset})

    return orm_filters

def apply_filters(self, request, applicable_filters):
    if 'custom' in applicable_filters:
        custom = applicable_filters.pop('custom')
    else:
        custom = None

    semi_filtered = super(BusinessResource, self).apply_filters(request, applicable_filters)

    return semi_filtered.filter(custom) if custom else semi_filtered

Поскольку вы используете объекты Q, стандартapply_filters Метод не достаточно умен, чтобы применить пользовательский ключ фильтра (поскольку его нет), однако вы можете быстро переопределить его и добавить специальный фильтр под названием & quot; custom & quot ;. При этом вашbuild_filters может найти соответствующий фильтр, сконструировать его значение и передать его как custom для apply_filters, который будет просто применять его напрямую, а не пытаться распаковать его значение из словаря как элемента.

 05 дек. 2012 г., 23:06
В словаре нет метода «продлить». Должно быть: orm_filters.update ({"пользовательский": qset})
 nknganda15 апр. 2012 г., 22:53
Это отлично работает. Спасибо
 24 сент. 2013 г., 00:16
Это решение вызывает двойной вызов БД (для semi_filtered и затем для пользовательского фильтра). Немного другой код работает для меня: если «пользовательский»; в apply_filters: custom = Applic_filters.pop ('custom' ') return Outreaches.objects.filter (custom) else: вернуть super (OutreachResource, self) .apply_filters (запрос, apply_filters)

Ваш ответ на вопрос