我有一个基本的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
并且在Meta for tastypie中,我将过滤设置为:
filtering = {
'name: ALL,
'description': ALL,
'email': ALL,
'query': ['icontains',],
}
我有什么想法可以解决这个问题吗?
由于 - 牛顿
答案 0 :(得分:41)
你走在正确的轨道上。但是,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
方法不够智能,无法应用自定义过滤器键(因为没有),但是您可以快速覆盖它并添加一个名为“custom”的特殊过滤器。在这样做的过程中,build_filters
可以找到一个合适的过滤器,构造它的含义并将其作为自定义传递给apply_filters,它将直接应用它而不是尝试将其值从字典中解压缩为项目。
答案 1 :(得分:0)
我这样解决了这个问题:
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
这种方法比我见过的其他方法感觉更清晰。
答案 2 :(得分:0)
根据astevanovic的回答并清理一下这个想法,以下内容应该有效并且更简洁。
主要区别在于,使用None
作为密钥而不是custom
(可能与列名冲突),apply_filters变得更强大。
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()