У меня есть представление 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 Filter, чтобы мы могли здесь что-то сделать.
Итак, проблема здесь в методе is_valid()
введенном как часть # 788.
Это автоматически возвращает False
когда data
равно None
.
Затем в представлении мы устанавливаем object_list
на qs.none()
.
Отсюда наблюдаемое поведение.
Тест на это не дает сбоев, потому что в тестовом шаблоне используется filter.qs
а не object_list
.
Непосредственный обходной путь - установить strict = False
в вашем наборе фильтров. Это приводит к тому, что фактические недопустимые параметры фильтра приведут к частичной фильтрации, а не к отображению пустых результатов, но это должно быть работоспособным на данный момент. (Лучше, конечно, переопределить логику представления get
...)
Мы подумаем и улучшим обработку несвязанного дела.
Хм. Основным недостатком здесь является несоответствие между object_list
и .qs
набора фильтров. Это исправит перенос строгого поведения обратно на FilterSet.qs
.
Кроме того, для бэкэнда DRF нет строгой / нестрогой обработки.
Я это сделаю.
@rpkilby Есть ли прогресс по этому вопросу?
Ага - начал PR, но пришлось приостановить мою работу с открытым исходным кодом. Я скоро смогу продолжить с того места, на котором остановился.
Обратите внимание, что обходной путь должен заключаться в установке strict = False
для вашего потомка FilterMixin
(возможно, 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, но пришлось приостановить мою работу с открытым исходным кодом. Я скоро смогу продолжить с того места, на котором остановился.