Django-filter: (2.0) FilterView всегда возвращает пустой QuerySet для несвязанного FilterSet.

Созданный на 29 июн. 2018  ·  19Комментарии  ·  Источник: carltongibson/django-filter

У меня есть представление 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 , я вижу, что фильтры не установлены:
enter image description here

Но затем, если я сделаю 127.0.0.1:8000?page=1 , я вижу, что фильтры инициализированы правильно:

enter image description here

Что заставляет мои фильтры не иметь значения по умолчанию, если к моему URL-адресу не добавлено page = 1?

Самый полезный комментарий

Ага - начал PR, но пришлось приостановить мою работу с открытым исходным кодом. Я скоро смогу продолжить с того места, на котором остановился.

Все 19 Комментарий

Попробуйте использовать фактическое логическое значение 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/

image

Несмотря на то, что фильтр был отображен с правильными значениями, фильтр не возымел эффекта, так как я все еще могу видеть приостановленные или заброшенные учетные записи.

Более того, когда я перехожу на страницу 2, нажимая кнопку 2 внизу, я попадаю в http://127.0.0.1:8000/?page=2 а фильтры сбрасываются на Unknown .

image

Вы видите причину, почему?

Извините - ничего очевидного в голову не приходит.

По первому вопросу я бы рекомендовал взглянуть на полученный 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 .

https://github.com/carltongibson/django-filter/blob/b1f1c6592ef64a2172ec39040607a9c0b0714140/django_filters/views.py#L39 -L46

Пусто ли data как для первой, так и для второй страницы?

Это не решается как есть. Закрытие ожидающих получения информации, достаточной для выявления проблемы.

Та же проблема здесь после обновления до 2.0. Для получения результатов в URL-адресе должна присутствовать либо страница по умолчанию, либо пустой фильтр. Это хоть где-то задокументировано?

Все еще нужна дополнительная информация, чтобы иметь возможность воспроизводить ...

page ? Разве это не связано с вашей разбивкой на страницы, а не с фильтром Django?

Какая именно информация нужна? Обновление до 2.0 приводит к описанному поведению. При переходе на версию 1.1.0 все возвращается в нормальное состояние. Я попытался упростить представление и набор фильтров до минимума, насколько это возможно, поведение сохраняется, поэтому кажется, что это должно быть довольно легко воспроизвести.

@moorchegue , будет

Также похоже, что проблема в том, что обеспечивает вашу логику разбиения на страницы. Да, может быть некоторая несовместимость с новой версией, но проблемы отладки в других пакетах выходят за рамки. Вам нужно продемонстрировать ошибку в Django Filter, чтобы мы могли здесь что-то сделать.

Итак, проблема здесь в методе is_valid() введенном как часть # 788.

https://github.com/carltongibson/django-filter/blob/1dde11c70eedeac32f6de92c426deb289e1aebd8/django_filters/filterset.py#L202 -L206

Это автоматически возвращает False когда data равно None .

Затем в представлении мы устанавливаем object_list на qs.none() .

https://github.com/carltongibson/django-filter/blob/1dde11c70eedeac32f6de92c426deb289e1aebd8/django_filters/views.py#L80 -L83

Отсюда наблюдаемое поведение.

Тест на это не дает сбоев, потому что в тестовом шаблоне используется filter.qs а не object_list .

https://github.com/carltongibson/django-filter/blob/1dde11c70eedeac32f6de92c426deb289e1aebd8/tests/templates/tests/book_filter.html#L3 -L5

Непосредственный обходной путь - установить 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

Спасибо.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги