Django的Tastypie高级过滤:如何做到符合Q对象的复杂查询(Django Tastypie

2019-06-23 10:03发布

我有一个基本的Django模型,如:

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)

我需要对上述模型进行复杂的查询,如:

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

我曾尝试以下使用tastypie没有运气:

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

在课堂上为元我tastypie已滤波设置为:

filtering = {
        'name: ALL,
        'description': ALL,
        'email': ALL,
        'query': ['icontains',],
    }

任何想法我怎么能解决呢?

谢谢 - 牛顿

Answer 1:

你是在正确的轨道上。 然而, 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

因为你是用品质的对象,标准apply_filters方法是没有足够的智慧应用自定义过滤器键(因为都没有),但您可以快速覆盖它并添加一个名为“定制”特殊的过滤器。 这样做你build_filters能找到一个合适的滤波器,构建这意味着什么,把它作为习惯,这将简单地应用它,而不是直接试图从字典作为项目解开它的价值apply_filters。



Answer 2:

我喜欢解决这样这个问题:

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

这种方法感觉比我见过别人的关注清晰分离。



Answer 3:

以在astevanovic的回答思路和清洁它一点,下面的工作应该是更加简洁。

主要的区别是,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()


文章来源: Django Tastypie Advanced Filtering: How to do complex lookups with Q objects