或在django过滤器中使用关系时过滤器的定义
我有三个模型,其简单关系如下:或在django过滤器中使用关系时过滤器的定义,django,django-rest-framework,django-filter,django-filters,Django,Django Rest Framework,Django Filter,Django Filters,我有三个模型,其简单关系如下: http://address/persons?start_time_0=2000-02-01&start_time_1=2000-03-01&billing_status=DE models.py views.py 我想从个人端点获取账单,该端点在账单中具有DE状态,并且在一段时间内: api/persons?start_time_0=2018-03-20&start_time_1=2018-03-23&billing_statu
http://address/persons?start_time_0=2000-02-01&start_time_1=2000-03-01&billing_status=DE
models.py
views.py
我想从个人端点获取账单,该端点在账单中具有DE
状态,并且在一段时间内:
api/persons?start_time_0=2018-03-20&start_time_1=2018-03-23&billing_status=DE
但结果并不是我想要的,它返回所有人在该期间都有一个会话,并且有一个状态为DE
的账单,无论该账单是否在该期间
换句话说,似乎在两个过滤器字段之间使用或操作,我认为与此问题有关,但目前我找不到获得所需结果的方法。我正在使用djang 1.10.3
编辑
我试着写一个例子来说明我需要什么以及我从django过滤器中得到了什么。如果我在示例中使用下面的查询得到persons,我只得到两个人:
select *
from
test_filter_person join test_filter_personsession on test_filter_person.id=test_filter_personsession.person_id join test_filter_billing on test_filter_personsession.id=test_filter_billing.session_id
where
start_time > '2000-02-01' and start_time < '2000-03-01' and status='DE';
编辑2
这是我在示例中的查询所依据的数据,使用这些数据,您可以看到我在上面提到的查询中必须返回的内容:
id | first_name | last_name | id | start_time | end_time | person_id | id | status | session_id
----+------------+-----------+----+---------------------------+---------------------------+-----------+----+--------+------------
0 | person | 0 | 0 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 | 0 | 0 | DE | 0
0 | person | 0 | 1 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 | 0 | 1 | BA | 1
0 | person | 0 | 2 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 | 0 | 2 | DE | 2
1 | person | 1 | 3 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 | 1 | 3 | BA | 3
1 | person | 1 | 4 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 | 1 | 4 | DE | 4
1 | person | 1 | 5 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 | 1 | 5 | DE | 5
2 | person | 2 | 6 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 | 2 | 6 | DE | 6
2 | person | 2 | 7 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 | 2 | 7 | DE | 7
2 | person | 2 | 8 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 | 2 | 8 | BA | 8
编辑3
我尝试使用prefetch\u related
连接表并获得预期的结果,因为我认为额外的连接会导致此问题,但这不起作用,我仍然得到相同的结果,并且没有任何效果
编辑4
这也有同样的问题。我还没有解决方案;但我认为,在工作中,简明的问题总结会比我的头脑更聪明!
据我所知;您的核心问题是两个先决条件的结果:
在相关模型上定义了两个离散过滤器;导致过滤器跨越多值关系
FilterSet
实现过滤的方式
让我们更详细地了解这些:
跨越多值关系的过滤器
这是更好地理解问题先决条件#1的重要资源:
本质上,start\u time
过滤器向查询集添加一个.filter(sessions\u start\u time=value)
,而billing\u status
过滤器向过滤器添加一个.filter(sessions\u billing\u status=value)
。这导致了上面描述的“跨越多值关系”问题,这意味着它将在这些过滤器之间执行或
,而不是按照您的要求执行和
这让我想,为什么我们不在开始时间过滤器中看到同样的问题;但这里的诀窍是它被定义为一个DateFromToRangeFilter
;它在内部使用带有\uu range=
结构的单个筛选器查询。如果它改为sessions\uuu start\u time\uu gt=
和sessions\uu start\u time\uu lt=
,我们这里也会遇到同样的问题
FilterSet
实现过滤的方式
谈话是廉价的;给我看看代码
如您所见,qs
属性通过迭代Filter
对象列表来解析,将初始qs依次传递给每个对象并返回结果。请参见qs=filter\uu.filter(qs,value)
这里的每个Filter
对象定义了一个特定的def Filter
操作,该操作基本上接受Queryset,然后向其添加一个连续的.Filter
下面是一个来自BaseFilter
类的示例
def filter(self, qs, value):
if isinstance(value, Lookup):
lookup = six.text_type(value.lookup_type)
value = value.value
else:
lookup = self.lookup_expr
if value in EMPTY_VALUES:
return qs
if self.distinct:
qs = qs.distinct()
qs = self.get_method(qs)(**{'%s__%s' % (self.name, lookup): value})
return qs
重要的代码行是:qs=self.get_方法(qs)(**{'%s_uu%s%%(self.name,lookup):value})
因此,这两个先决条件为这个问题创造了完美的风暴。这对我来说很有效:
类FooFilterSet(FilterSet):
def过滤器查询集(自身,查询集):
"""
重写基本methtod,以便使用多个`.filter()在queryset上进行迭代`
调用,每个筛选器一个,它将累积查找表达式并在单个筛选器中应用它们
`.filter()`调用-在多对多关系中使用显式“AND”进行筛选。
"""
筛选器_kwargs={}
对于名称,使用self.form.data.items()中的值:
如果值不在空值中:
查找=“%s\uuu%s%”(self.filters[name]。字段名称,self.filters[name]。查找表达式)
filter_kwargs.update({lookup:value})
queryset=queryset.filter(**过滤器)
断言isinstance(queryset,models.queryset)\
预期'%s.%s'返回查询集,但得到的是%s\
%(类型(自身)。\uuuuu名称,名称,类型(查询集)。\uuuuu名称\uuuuuu)
返回查询集
重写filter\u queryset
方法,使其累积表达式并在单个.filter()
调用中应用它们在filters.py文件中而不是在views.py中对过滤器进行克隆。查看“设置”中的“已安装的应用程序”列表中是否有django筛选器。pPerson
如何与PersonSession
或计费模式相关?上面的代码看起来不完整。根据给定的信息,我可以得出的结论是,您的e正在将PersonSession
与计费
(计费具有会话
属性)和人
(假设分支会话
反向映射与过滤器中使用的映射相关联)。尝试使用此人拥有的帐单进行筛选。类似的方法应该可以工作-start\u time=django\u filters.datefromtrangefilter(name='billings\u sessions\u\u start\u time',distinct=True)
假设Person
有一个属性billing
。如果有帮助,请告诉我。对于模型中的问题,我很抱歉,我忘记了模型中的一些字段<代码>会话
有一个指向人员的外键
,计费
与会话
有一对一的关系。使用这些假设,我认为我的过滤器没有问题,必须按照我的预期工作,但事实并非如此。@GeancarloMurillo我不明白为什么更改过滤器实现所在的文件和位置会影响过滤器的结果?正如我所解释的,我从过滤器中得到了一些结果,但是
id | first_name | last_name | id | start_time | end_time | person_id | id | status | session_id
----+------------+-----------+----+---------------------------+---------------------------+-----------+----+--------+------------
0 | person | 0 | 0 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 | 0 | 0 | DE | 0
0 | person | 0 | 1 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 | 0 | 1 | BA | 1
0 | person | 0 | 2 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 | 0 | 2 | DE | 2
1 | person | 1 | 3 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 | 1 | 3 | BA | 3
1 | person | 1 | 4 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 | 1 | 4 | DE | 4
1 | person | 1 | 5 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 | 1 | 5 | DE | 5
2 | person | 2 | 6 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 | 2 | 6 | DE | 6
2 | person | 2 | 7 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 | 2 | 7 | DE | 7
2 | person | 2 | 8 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 | 2 | 8 | BA | 8
@property
def qs(self):
if not hasattr(self, '_qs'):
if not self.is_bound:
self._qs = self.queryset.all()
return self._qs
if not self.form.is_valid():
if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
raise forms.ValidationError(self.form.errors)
elif self.strict == STRICTNESS.RETURN_NO_RESULTS:
self._qs = self.queryset.none()
return self._qs
# else STRICTNESS.IGNORE... ignoring
# start with all the results and filter from there
qs = self.queryset.all()
for name, filter_ in six.iteritems(self.filters):
value = self.form.cleaned_data.get(name)
if value is not None: # valid & clean data
qs = filter_.filter(qs, value)
self._qs = qs
return self._qs
def filter(self, qs, value):
if isinstance(value, Lookup):
lookup = six.text_type(value.lookup_type)
value = value.value
else:
lookup = self.lookup_expr
if value in EMPTY_VALUES:
return qs
if self.distinct:
qs = qs.distinct()
qs = self.get_method(qs)(**{'%s__%s' % (self.name, lookup): value})
return qs