Eu tenho um modo de exibição, AccountList
, que está tentando renderizar uma tabela django_table2. O código-fonte da visualização:
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
Esta visualização está usando este conjunto de filtros (de 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)
Usando este modelo:
{% if filter %}
<form action="" method="get" class="form form-inline">
{{ filter.form.as_p }}
<input type="submit" />
</form>
{% endif %}
{% render_table object_list %}
{% endblock %}
Este é meu de meu urls.py
path('', login_required(AccountList.as_view())),
Quando visito minha página, 127.0.0.1:8000
, vejo que os filtros não estão definidos:
Mas então, se eu fizer 127.0.0.1:8000?page=1
, vejo que os filtros foram inicializados corretamente:
Tente usar um valor booleano False
real em vez do valor da string 'False'
.
Então, quando tentei o seguinte:
is_suspended = django_filters.BooleanFilter(name='is_suspended', initial=False)
is_abandoned = django_filters.BooleanFilter(name='is_abandoned', initial=False)
É assim que os filtros são renderizados em 127.0.0.1:8000/
Embora o filtro tenha sido renderizado com os valores corretos, o filtro não teve efeito, pois ainda posso ver contas suspensas ou abandonadas.
Além disso, quando visito a página 2 pressionando o botão 2
na parte inferior, chego a http://127.0.0.1:8000/?page=2
com os filtros redefinidos para Unknown
.
Você vê alguma razão para isso?
Desculpe - nada óbvio vem à mente.
Para o primeiro problema, eu recomendo olhar a consulta SQL resultante. Certifique-se de que os filtros suspended
e abandoned
estejam realmente sendo aplicados.
Para o segundo problema, eu verificaria os dados de entrada. É possível que um valor inválido esteja sendo fornecido aos filtros suspended
e abandoned
ao alterar as páginas.
Inseri algumas declarações de impressão e encontrei o seguinte:
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)
Portanto, parece que data
é None
. Você saberá por que minha visão baseada em classe não está passando data
para AccountFilter
?
Hm. O argumento data
deve sempre ser fornecido por FilterView
.
data
vazio para a primeira e a segunda página?
Isso não é endereçável como está. Fechamento pendente com informações suficientes para identificar um problema.
O mesmo problema aqui após a atualização para 2.0. A página padrão ou o filtro vazio devem estar presentes no URL para obter os resultados. Isso está pelo menos documentado em algum lugar?
Ainda preciso de mais informações para poder reproduzir ...
page
? Isso não tem a ver com sua paginação, em vez do Filtro Django?
Quais informações são necessárias exatamente? Atualizar para 2.0 leva ao comportamento descrito. O downgrade para 1.1.0 faz com que tudo volte ao normal. Tentei simplificar a visualização e o conjunto de filtros para o mais minimalista possível, o comportamento persiste, então parece que isso deve ser bastante fácil de reproduzir.
@moorchegue , um caso de teste de exemplo mínimo ou projeto de teste que demonstre o problema seria útil.
Também parece que o problema está em tudo o que está fornecendo sua lógica de paginação. Sim, pode haver alguma incompatibilidade com a nova versão, mas a depuração de problemas em outros pacotes está fora do escopo. Você precisa demonstrar um bug no Filtro do Django para que haja algo que possamos fazer aqui.
OK, então o problema aqui é com o método is_valid()
introduzido como parte de # 788.
Isso retorna automaticamente False
quando data
for None
.
Na visualização, estamos definindo object_list
como qs.none()
.
Daí o comportamento observado.
O teste para isso não está falhando porque o modelo de teste está usando filter.qs
vez de object_list
.
A solução imediata é definir strict = False
em seu conjunto de filtros. Isso tem a conseqüência de que parâmetros de filtro inválidos reais levarão à filtragem parcial em vez de mostrar resultados vazios, mas deve ser viável por enquanto. (Melhor é substituir a lógica de visão get
, é claro ...)
Teremos uma reflexão e melhoraremos o manuseio do case não acoplado.
Hm. A deficiência principal aqui é a disparidade entre o Ver object_list
de eo filterset .qs
. Mover o comportamento estrito de volta para FilterSet.qs
consertaria isso.
Além disso, não há tratamento estrito / não estrito para o back-end DRF.
Vou limpar isso.
@rpkilby Há algum progresso nessa questão?
Sim - comecei o PR, mas tive que colocar meu trabalho de código aberto em espera. Devo ser capaz de retomar de onde parei em breve.
Observe que a solução alternativa deve ser definir strict = False
em seu FilterMixin
descendente (provavelmente o View
.
Você não pode simplesmente alterar a condição para retornar o qs no caso de forma ilimitada?
por exemplo
- 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
Obrigado.
Comentários muito úteis
Sim - comecei o PR, mas tive que colocar meu trabalho de código aberto em espera. Devo ser capaz de retomar de onde parei em breve.