From f744256f3bf80f33e911cceccc28d08789e9f545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A6var=20=C3=96fj=C3=B6r=C3=B0=20Magn=C3=BAsson?= Date: Thu, 13 Jun 2024 12:09:18 +0200 Subject: [PATCH 1/4] Fix grouped choices as dict --- django_filters/filters.py | 14 ++++++++++++++ tests/test_filters.py | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/django_filters/filters.py b/django_filters/filters.py index a024e9b0..98c33d71 100644 --- a/django_filters/filters.py +++ b/django_filters/filters.py @@ -30,6 +30,14 @@ ) from .utils import get_model_field, label_for_filter +try: + from django.utils.choices import BaseChoiceIterator, normalize_choices +except ImportError: + DJANGO_50 = False +else: + DJANGO_50 = True + + __all__ = [ "AllValuesFilter", "AllValuesMultipleFilter", @@ -479,6 +487,12 @@ def __init__(self, choices=None, filters=None, *args, **kwargs): if filters is not None: self.filters = filters + if isinstance(self.choices, dict): + if DJANGO_50: + self.choices = normalize_choices(self.choices) + else: + assert not DJANGO_50, "Django 5.0 or later is required for dict choices" + all_choices = list( chain.from_iterable( [subchoice[0] for subchoice in choice[1]] diff --git a/tests/test_filters.py b/tests/test_filters.py index 290fbc13..5511f927 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -1105,6 +1105,11 @@ def test_choices_with_optgroups_dont_mistmatch(self): choices=[("group", ("a", "a")), ("b", "b")], filters={"a": None, "b": None} ) + def test_grouped_choices_as_dictionary(self): + DateRangeFilter( + choices={"group": {"a": "a", "b": "b"}}, filters={"a": None, "b": None} + ) + def test_filtering_for_this_year(self): qs = mock.Mock(spec=["filter"]) with mock.patch("django_filters.filters.now") as mock_now: From d26e7f82668ce2583a550e26654353cbadd2e14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A6var=20=C3=96fj=C3=B6r=C3=B0=20Magn=C3=BAsson?= Date: Fri, 19 Jul 2024 14:09:30 +0000 Subject: [PATCH 2/4] Attempt to fix failing test --- tests/test_filters.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_filters.py b/tests/test_filters.py index 5511f927..0c06e2e4 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -1,8 +1,10 @@ import inspect +import unittest from collections import OrderedDict from datetime import date, datetime, time, timedelta from unittest import mock +import django from django import forms from django.test import TestCase, override_settings from django.utils import translation @@ -1105,6 +1107,7 @@ def test_choices_with_optgroups_dont_mistmatch(self): choices=[("group", ("a", "a")), ("b", "b")], filters={"a": None, "b": None} ) + @unittest.skipUnless(django.VERSION >= (5, 0), "Django 5.0 introduced new dictionary choices option") def test_grouped_choices_as_dictionary(self): DateRangeFilter( choices={"group": {"a": "a", "b": "b"}}, filters={"a": None, "b": None} From af84991e387bb40699d1eaff14bd12061f98bd40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A6var=20=C3=96fj=C3=B6r=C3=B0=20Magn=C3=BAsson?= Date: Fri, 19 Jul 2024 14:19:38 +0000 Subject: [PATCH 3/4] Remove unused import --- django_filters/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_filters/filters.py b/django_filters/filters.py index 98c33d71..f19c4f64 100644 --- a/django_filters/filters.py +++ b/django_filters/filters.py @@ -31,7 +31,7 @@ from .utils import get_model_field, label_for_filter try: - from django.utils.choices import BaseChoiceIterator, normalize_choices + from django.utils.choices import normalize_choices except ImportError: DJANGO_50 = False else: From ba88cb0644510a9d732d6f20489588091059bfa1 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Fri, 2 Aug 2024 15:10:09 +0200 Subject: [PATCH 4/4] Test (and fix) raising error on Django 4.2. --- django_filters/filters.py | 2 +- tests/test_filters.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/django_filters/filters.py b/django_filters/filters.py index f19c4f64..cada331c 100644 --- a/django_filters/filters.py +++ b/django_filters/filters.py @@ -491,7 +491,7 @@ def __init__(self, choices=None, filters=None, *args, **kwargs): if DJANGO_50: self.choices = normalize_choices(self.choices) else: - assert not DJANGO_50, "Django 5.0 or later is required for dict choices" + raise ValueError("Django 5.0 or later is required for dict choices") all_choices = list( chain.from_iterable( diff --git a/tests/test_filters.py b/tests/test_filters.py index 0c06e2e4..bcbf9c17 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -1113,6 +1113,16 @@ def test_grouped_choices_as_dictionary(self): choices={"group": {"a": "a", "b": "b"}}, filters={"a": None, "b": None} ) + @unittest.skipUnless(django.VERSION <= (4, 2), "Django 5.0 introduced new dictionary choices option") + def test_grouped_choices_error(self): + with self.assertRaisesMessage( + ValueError, + "Django 5.0 or later is required for dict choices" + ): + DateRangeFilter( + choices={"group": {"a": "a", "b": "b"}}, filters={"a": None, "b": None} + ) + def test_filtering_for_this_year(self): qs = mock.Mock(spec=["filter"]) with mock.patch("django_filters.filters.now") as mock_now: