我有一个视图AccountList
,它试图呈现一个 django_table2 表。 视图的源代码:
class AccountList(SingleTableMixin, FilterView):
model = Account
table_class = AccountTable
template_name = 'accounts/account_list.html'
context_table_name = 'object_list'
ordering = ['vps']
filterset_class = AccountFilter
此视图当前正在使用此过滤器集(来自 django_filters):
import django_filters
from accounts.models import Account
class AccountFilter(django_filters.FilterSet):
class Meta:
model = Account
fields = ['is_suspended', 'is_abandoned']
is_suspended = django_filters.BooleanFilter(name='is_suspended', initial='False')
is_abandoned = django_filters.BooleanFilter(name='is_abandoned', initial='False')
def __init__(self, data=None, *args, **kwargs):
# if filterset is bound, use initial values as defaults
if data is not None:
# get a mutable copy of the QueryDict
data = data.copy()
for name, f in self.base_filters.items():
initial = f.extra.get('initial')
# filter param is either missing or empty, use initial as default
if not data.get(name) and initial:
data[name] = initial
super().__init__(data, *args, **kwargs)
使用这个模板:
{% if filter %}
<form action="" method="get" class="form form-inline">
{{ filter.form.as_p }}
<input type="submit" />
</form>
{% endif %}
{% render_table object_list %}
{% endblock %}
这是我的 urls.py
path('', login_required(AccountList.as_view())),
当我访问我的页面127.0.0.1:8000
,我看到未设置过滤器:
但是,如果我执行127.0.0.1:8000?page=1
,我会看到过滤器已正确初始化:
尝试使用实际的False
布尔值而不是字符串值'False'
。
因此,当我尝试以下操作时:
is_suspended = django_filters.BooleanFilter(name='is_suspended', initial=False)
is_abandoned = django_filters.BooleanFilter(name='is_abandoned', initial=False)
这就是过滤器在127.0.0.1:8000/
上的呈现方式
即使过滤器使用正确的值呈现,过滤器也不起作用,因为我仍然可以看到被暂停或放弃的帐户。
此外,当我通过按下底部的2
按钮访问第 2 页时,我会到达http://127.0.0.1:8000/?page=2
并将过滤器重置为Unknown
。
你看出什么原因了吗?
对不起 - 没有什么明显的想到。
对于第一个问题,我建议查看生成的 SQL 查询。 确保实际应用了suspended
和abandoned
过滤器。
对于第二个问题,我会检查传入的数据。 更改页面时,可能会向suspended
和abandoned
过滤器提供无效值。
我插入了一些打印语句,发现以下内容:
def __init__(self, data=None, *args, **kwargs):
if data is not None: # 1
data = data.copy()
for name, f in self.base_filters.items():
initial = f.extra.get('initial')
# filter param is either missing or empty, use initial as default
if not data.get(name) and initial:
data[name] = initial
super(BaseFilterSet, self).__init__(data, *args, **kwargs)
所以看起来data
是None
。 你会知道为什么我的基于类的视图没有将data
传递给AccountFilter
吗?
嗯。 data
参数应始终由FilterView
。
第一页和第二页的data
空的吗?
这不能按原样解决。 关闭待处理的足够信息以识别问题。
升级到 2.0 后同样的问题。 URL 中应存在默认页面或空过滤器以获取结果。 这至少在某处记录了吗?
仍然需要更多信息才能重现...
page
? 这不是与您的分页有关,而不是与 Django 过滤器有关吗?
具体需要什么信息? 升级到 2.0 会导致所描述的行为。 降级到 1.1.0 使一切恢复正常。 我尝试将视图和过滤器集简化为尽可能简约,行为仍然存在,因此看起来这应该很容易重现。
@moorchegue ,演示该问题的最小示例测试用例或测试项目会有所帮助。
看起来问题出在提供分页逻辑的任何内容上。 是的,可能与新版本存在一些不兼容,但在其他包中调试问题超出了范围。 您需要在 Django 过滤器中演示一个错误,以便我们可以在这里做任何事情。
好的,所以这里的问题是作为#788 的一部分引入的is_valid()
方法。
当data
是None
时,它会自动返回False
None
。
在视图中,我们将object_list
为qs.none()
。
因此观察到的行为。
对此的测试不会失败,因为测试模板使用的是filter.qs
而不是object_list
。
直接的解决方法是在您的过滤器集上设置strict = False
。 这导致实际无效的过滤器参数将导致部分过滤而不是显示空结果,但它现在应该是可行的。 (当然最好是覆盖视图get
逻辑......)
我们会思考并改进对未绑定案例的处理。
嗯。 这里的主要缺陷是 View 的object_list
和 FilterSet 的.qs
之间的差异。 将严格行为移回FilterSet.qs
可以解决这个问题。
此外,DRF 后端没有严格/非严格处理。
我会解决这个问题。
@rpkilby这个问题有什么进展吗?
是的 - 开始了 PR 但不得不搁置我的开源工作。 我应该能够很快从我离开的地方开始。
请注意,解决方法应该是在您的FilterMixin
后代上设置strict = False
(可能是View
。
在无界形式的情况下,您不能更改条件以返回 qs 吗?
例如
- if self.filterset.is_valid() or not self.get_strict():
+ if not self.filterset.is_bound or self.filterset.is_valid() or not self.get_strict():
https://github.com/carltongibson/django-filter/pull/1007
谢谢。
最有用的评论
是的 - 开始了 PR 但不得不搁置我的开源工作。 我应该能够很快从我离开的地方开始。