From b469fa2c3dbfc041d24a658b1adfc7d6e8447f6b Mon Sep 17 00:00:00 2001 From: Joaquim Nallar Date: Wed, 25 Oct 2023 17:55:25 +0200 Subject: [PATCH 01/12] Hot fix provider_choices --- geotrek/common/mixins/managers.py | 10 +++ geotrek/core/filters.py | 12 +++- geotrek/core/managers.py | 16 ++--- geotrek/core/tests/test_views.py | 51 +++++++++++++++ geotrek/infrastructure/filters.py | 3 +- geotrek/infrastructure/managers.py | 9 +-- geotrek/infrastructure/tests/test_views.py | 26 ++++++++ geotrek/outdoor/filters.py | 12 +++- geotrek/outdoor/managers.py | 16 ++--- geotrek/outdoor/tests/test_views.py | 51 +++++++++++++++ geotrek/sensitivity/filters.py | 6 +- geotrek/sensitivity/managers.py | 9 +-- geotrek/sensitivity/tests/test_views.py | 26 ++++++++ geotrek/signage/filters.py | 3 +- geotrek/signage/managers.py | 12 ++-- geotrek/signage/tests/test_views.py | 21 ++++++ geotrek/tourism/filters.py | 12 +++- geotrek/tourism/managers.py | 16 ++--- geotrek/tourism/tests/test_views.py | 51 +++++++++++++++ geotrek/trekking/filters.py | 18 ++++- geotrek/trekking/managers.py | 23 ++----- geotrek/trekking/tests/test_views.py | 76 ++++++++++++++++++++++ 22 files changed, 399 insertions(+), 80 deletions(-) diff --git a/geotrek/common/mixins/managers.py b/geotrek/common/mixins/managers.py index 25bd7aa388..69e6baff7d 100644 --- a/geotrek/common/mixins/managers.py +++ b/geotrek/common/mixins/managers.py @@ -17,3 +17,13 @@ def get_queryset(self): # Filter out deleted objects def existing(self): return self.get_queryset().filter(deleted=False) + + +class ProviderChoicesMixin: + def provider_choices(self): + qs = self.get_queryset() + if hasattr(qs, "existing"): + qs = qs.existing() + values = qs.exclude(provider__exact='') \ + .distinct('provider').order_by("provider").values_list('provider', flat=True) + return tuple((value, value) for value in values) diff --git a/geotrek/core/filters.py b/geotrek/core/filters.py index 37ff16f426..9a2e941c4f 100644 --- a/geotrek/core/filters.py +++ b/geotrek/core/filters.py @@ -103,7 +103,7 @@ class PathFilterSet(AltimetryAllGeometriesFilterSet, ZoningFilterSet, StructureR field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=Path.objects.provider_choices() + choices=(('', '---------'),) ) networks = ModelMultipleChoiceFilter(queryset=Network.objects.all().select_related("structure")) usages = ModelMultipleChoiceFilter(queryset=Usage.objects.all().select_related("structure")) @@ -114,6 +114,10 @@ class Meta(StructureRelatedFilterSet.Meta): fields = StructureRelatedFilterSet.Meta.fields + \ ['valid', 'networks', 'usages', 'comfort', 'stake', 'draft', 'provider'] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = Path.objects.provider_choices() + class TrailFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilterSet, ZoningFilterSet, StructureRelatedFilterSet): """Trail filter set""" @@ -130,7 +134,7 @@ class TrailFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilterSet, Zo field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=Trail.objects.provider_choices() + choices=(('', '---------'),) ) class Meta(StructureRelatedFilterSet.Meta): @@ -138,6 +142,10 @@ class Meta(StructureRelatedFilterSet.Meta): fields = StructureRelatedFilterSet.Meta.fields + \ ['name', 'category', 'departure', 'arrival', 'certification_labels', 'comments', 'provider'] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = Trail.objects.provider_choices() + class TopologyFilterTrail(TopologyFilter): queryset = Trail.objects.existing() diff --git a/geotrek/core/managers.py b/geotrek/core/managers.py index b77c034c7a..143a28d224 100644 --- a/geotrek/core/managers.py +++ b/geotrek/core/managers.py @@ -1,10 +1,10 @@ from django.contrib.gis.db import models from geotrek.common.functions import Length -from geotrek.common.mixins.managers import NoDeleteManager +from geotrek.common.mixins.managers import NoDeleteManager, ProviderChoicesMixin -class PathManager(models.Manager): +class PathManager(models.Manager, ProviderChoicesMixin): # Use this manager when walking through FK/M2M relationships use_for_related_fields = True @@ -13,11 +13,6 @@ def get_queryset(self): """ return super().get_queryset().filter(visible=True).annotate(length_2d=Length('geom')) - def provider_choices(self): - providers = self.get_queryset().exclude(provider__exact='') \ - .distinct('provider').values_list('provider', 'provider') - return providers - class PathInvisibleManager(models.Manager): use_for_related_fields = True @@ -39,8 +34,5 @@ def get_queryset(self): return super().get_queryset().order_by('order') -class TrailManager(TopologyManager): - def provider_choices(self): - providers = self.get_queryset().existing().exclude(provider__exact='').order_by('provider') \ - .distinct('provider').values_list('provider', 'provider') - return providers +class TrailManager(TopologyManager, ProviderChoicesMixin): + pass diff --git a/geotrek/core/tests/test_views.py b/geotrek/core/tests/test_views.py index c8fa7ac5d4..3a1c1eb516 100644 --- a/geotrek/core/tests/test_views.py +++ b/geotrek/core/tests/test_views.py @@ -19,6 +19,7 @@ from geotrek.authent.tests.base import AuthentFixturesTest from geotrek.core.models import Path, Trail, PathSource +from geotrek.core.filters import PathFilterSet, TrailFilterSet from geotrek.trekking.tests.factories import POIFactory, TrekFactory, ServiceFactory from geotrek.infrastructure.tests.factories import InfrastructureFactory @@ -873,3 +874,53 @@ def test_remove_poi(self): self.assertEqual(poi.deleted, False) self.assertAlmostEqual(1.5, poi.offset) + + +class PathFilterTest(CommonTest, AuthentFixturesTest): + factory = PathFactory + filterset = PathFilterSet + + def test_provider_filter_without_provider(self): + filter_set = PathFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = PathFactory.create(provider='my_provider1') + signage2 = PathFactory.create(provider='my_provider2') + + filter_set = PathFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) + + +class TrailFilterTest(CommonTest, AuthentFixturesTest): + factory = TrailFactory + filterset = TrailFilterSet + + def test_provider_filter_without_provider(self): + filter_set = TrailFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = TrailFactory.create(provider='my_provider1') + signage2 = TrailFactory.create(provider='my_provider2') + + filter_set = TrailFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) diff --git a/geotrek/infrastructure/filters.py b/geotrek/infrastructure/filters.py index a5aa4150c5..1a88cc1e33 100644 --- a/geotrek/infrastructure/filters.py +++ b/geotrek/infrastructure/filters.py @@ -24,7 +24,7 @@ class InfrastructureFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilt field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=Infrastructure.objects.provider_choices() + choices=(('', '---------'),) ) class Meta(StructureRelatedFilterSet.Meta): @@ -43,3 +43,4 @@ def filter_intervention_year(self, qs, name, value): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.form.fields['implantation_year'].choices = Infrastructure.objects.implantation_year_choices() + self.form.fields['provider'].choices = Infrastructure.objects.provider_choices() diff --git a/geotrek/infrastructure/managers.py b/geotrek/infrastructure/managers.py index 09d8f0fbbd..7fba2faa8e 100644 --- a/geotrek/infrastructure/managers.py +++ b/geotrek/infrastructure/managers.py @@ -1,15 +1,10 @@ -from geotrek.common.mixins.managers import NoDeleteManager +from geotrek.common.mixins.managers import NoDeleteManager, ProviderChoicesMixin -class InfrastructureGISManager(NoDeleteManager): +class InfrastructureGISManager(NoDeleteManager, ProviderChoicesMixin): """ Override default typology mixin manager""" def implantation_year_choices(self): values = self.get_queryset().existing().filter(implantation_year__isnull=False)\ .order_by('-implantation_year').distinct('implantation_year') \ .values_list('implantation_year', flat=True) return tuple((value, value) for value in values) - - def provider_choices(self): - providers = self.get_queryset().existing().exclude(provider__exact='') \ - .distinct('provider').values_list('provider', 'provider') - return providers diff --git a/geotrek/infrastructure/tests/test_views.py b/geotrek/infrastructure/tests/test_views.py index b1ffa2371f..e24745e436 100755 --- a/geotrek/infrastructure/tests/test_views.py +++ b/geotrek/infrastructure/tests/test_views.py @@ -3,6 +3,7 @@ from geotrek.common.tests import CommonTest, GeotrekAPITestCase from geotrek.authent.tests.factories import PathManagerFactory from geotrek.infrastructure.models import (Infrastructure, INFRASTRUCTURE_TYPES) +from geotrek.infrastructure.filters import InfrastructureFilterSet from geotrek.core.tests.factories import PathFactory from geotrek.infrastructure.tests.factories import (InfrastructureFactory, InfrastructureNoPictogramFactory, InfrastructureTypeFactory, InfrastructureConditionFactory, @@ -116,3 +117,28 @@ def get_good_data(self): else: good_data['geom'] = 'POINT(0.42 0.666)' return good_data + + +class InfrastructureFilterTest(CommonTest): + factory = InfrastructureFactory + filterset = InfrastructureFilterSet + + def test_provider_filter_without_provider(self): + filter_set = InfrastructureFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = InfrastructureFactory.create(provider='my_provider1') + signage2 = InfrastructureFactory.create(provider='my_provider2') + + filter_set = InfrastructureFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) diff --git a/geotrek/outdoor/filters.py b/geotrek/outdoor/filters.py index 12fc390a63..ab10c540e3 100644 --- a/geotrek/outdoor/filters.py +++ b/geotrek/outdoor/filters.py @@ -17,7 +17,7 @@ class SiteFilterSet(ZoningFilterSet, StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=Site.objects.provider_choices() + choices=(('', '---------'),) ) class Meta(StructureRelatedFilterSet.Meta): @@ -27,6 +27,10 @@ class Meta(StructureRelatedFilterSet.Meta): 'web_links', 'type', 'orientation', 'wind', 'provider' ] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = Site.objects.provider_choices() + def filter_orientation(self, qs, name, values): q = Q() for value in values: @@ -58,7 +62,7 @@ class CourseFilterSet(ZoningFilterSet, StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=Course.objects.provider_choices() + choices=(('', '---------'),) ) class Meta(StructureRelatedFilterSet.Meta): @@ -69,6 +73,10 @@ class Meta(StructureRelatedFilterSet.Meta): 'height', 'provider' ] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = Course.objects.provider_choices() + def filter_orientation(self, qs, name, values): q = Q() for value in values: diff --git a/geotrek/outdoor/managers.py b/geotrek/outdoor/managers.py index 6dc0d082db..c5d1ac0aa6 100644 --- a/geotrek/outdoor/managers.py +++ b/geotrek/outdoor/managers.py @@ -2,12 +2,11 @@ from django.db.models import Manager from mptt.managers import TreeManager +from geotrek.common.mixins.managers import ProviderChoicesMixin -class SiteManager(TreeManager): - def provider_choices(self): - providers = self.get_queryset().exclude(provider__exact='').order_by('provider') \ - .distinct('provider').values_list('provider', 'provider') - return providers + +class SiteManager(TreeManager, ProviderChoicesMixin): + pass class CourseOrderedChildManager(models.Manager): @@ -18,8 +17,5 @@ def get_queryset(self): return super(CourseOrderedChildManager, self).get_queryset().select_related('parent', 'child') -class CourseManager(Manager): - def provider_choices(self): - providers = self.get_queryset().exclude(provider__exact='').order_by('provider') \ - .distinct('provider').values_list('provider', 'provider') - return providers +class CourseManager(Manager, ProviderChoicesMixin): + pass diff --git a/geotrek/outdoor/tests/test_views.py b/geotrek/outdoor/tests/test_views.py index f478cd3974..a0b060376e 100644 --- a/geotrek/outdoor/tests/test_views.py +++ b/geotrek/outdoor/tests/test_views.py @@ -11,6 +11,7 @@ TargetPortalFactory) from geotrek.outdoor import views as course_views from geotrek.outdoor.models import Site +from geotrek.outdoor.filters import SiteFilterSet, CourseFilterSet from geotrek.outdoor.tests.factories import CourseFactory, SiteFactory from geotrek.tourism.tests.test_views import PNG_BLACK_PIXEL from geotrek.trekking.tests.factories import POIFactory @@ -161,3 +162,53 @@ def test_delete_site(self): self.assertEqual(response.status_code, 302) self.assertEqual(Site.objects.count(), 1) self.assertEqual(Site.objects.filter(pk=site_1.pk).exists(), True) + + +class SiteFilterTest(TestCase): + factory = SiteFactory + filterset = SiteFilterSet + + def test_provider_filter_without_provider(self): + filter_set = SiteFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = SiteFactory.create(provider='my_provider1') + signage2 = SiteFactory.create(provider='my_provider2') + + filter_set = SiteFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) + + +class CourseFilterTest(TestCase): + factory = CourseFactory + filterset = CourseFilterSet + + def test_provider_filter_without_provider(self): + filter_set = CourseFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = CourseFactory.create(provider='my_provider1') + signage2 = CourseFactory.create(provider='my_provider2') + + filter_set = CourseFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) diff --git a/geotrek/sensitivity/filters.py b/geotrek/sensitivity/filters.py index 0b631e1b8c..27f277936d 100644 --- a/geotrek/sensitivity/filters.py +++ b/geotrek/sensitivity/filters.py @@ -13,9 +13,13 @@ class SensitiveAreaFilterSet(StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=SensitiveArea.objects.provider_choices() + choices=(('', '---------'),) ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = SensitiveArea.objects.provider_choices() + class Meta(StructureRelatedFilterSet.Meta): model = SensitiveArea fields = StructureRelatedFilterSet.Meta.fields + [ diff --git a/geotrek/sensitivity/managers.py b/geotrek/sensitivity/managers.py index 16b5c5bac6..f363eb40b3 100644 --- a/geotrek/sensitivity/managers.py +++ b/geotrek/sensitivity/managers.py @@ -1,8 +1,5 @@ -from geotrek.common.mixins.managers import NoDeleteManager +from geotrek.common.mixins.managers import NoDeleteManager, ProviderChoicesMixin -class SensitiveAreaManager(NoDeleteManager): - def provider_choices(self): - providers = self.get_queryset().existing().exclude(provider__exact='') \ - .distinct('provider').values_list('provider', 'provider') - return providers +class SensitiveAreaManager(NoDeleteManager, ProviderChoicesMixin): + pass diff --git a/geotrek/sensitivity/tests/test_views.py b/geotrek/sensitivity/tests/test_views.py index f0ebb92a9c..d25691374c 100644 --- a/geotrek/sensitivity/tests/test_views.py +++ b/geotrek/sensitivity/tests/test_views.py @@ -16,6 +16,7 @@ MultiPolygonSensitiveAreaFactory ) from geotrek.sensitivity.models import SportPractice +from geotrek.sensitivity.filters import SensitiveAreaFilterSet class SensitiveAreaViewsSameStructureTests(AuthentFixturesTest): @@ -371,3 +372,28 @@ def test_openair_list(self): '\n' 'AC ZSM\n' self.assertContains(response, expected_response) + + +class SensitiveAreaFilterTest(TestCase): + factory = SensitiveAreaFactory + filterset = SensitiveAreaFilterSet + + def test_provider_filter_without_provider(self): + filter_set = SensitiveAreaFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = SensitiveAreaFactory.create(provider='my_provider1') + signage2 = SensitiveAreaFactory.create(provider='my_provider2') + + filter_set = SensitiveAreaFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) diff --git a/geotrek/signage/filters.py b/geotrek/signage/filters.py index 879a0a8c6e..431360027f 100644 --- a/geotrek/signage/filters.py +++ b/geotrek/signage/filters.py @@ -35,7 +35,7 @@ class SignageFilterSet(AltimetryPointFilterSet, ValidTopologyFilterSet, ZoningFi field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=Signage.objects.provider_choices() + choices=(('', '---------'),) ) class Meta(StructureRelatedFilterSet.Meta): @@ -47,6 +47,7 @@ class Meta(StructureRelatedFilterSet.Meta): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.form.fields['implantation_year'].choices = Signage.objects.implantation_year_choices() + self.form.fields['provider'].choices = Signage.objects.provider_choices() def filter_intervention_year(self, qs, name, value): signage_ct = ContentType.objects.get_for_model(Signage) diff --git a/geotrek/signage/managers.py b/geotrek/signage/managers.py index 7e82df155e..1bc39e5a37 100644 --- a/geotrek/signage/managers.py +++ b/geotrek/signage/managers.py @@ -1,7 +1,7 @@ -from geotrek.common.mixins.managers import NoDeleteManager +from geotrek.common.mixins.managers import NoDeleteManager, ProviderChoicesMixin -class SignageGISManager(NoDeleteManager): +class SignageGISManager(NoDeleteManager, ProviderChoicesMixin): """ Override default typology mixin manager, and filter by type. """ def implantation_year_choices(self): values = self.get_queryset().existing().filter(implantation_year__isnull=False)\ @@ -9,7 +9,7 @@ def implantation_year_choices(self): .values_list('implantation_year', flat=True) return tuple((value, value) for value in values) - def provider_choices(self): - providers = self.get_queryset().existing().exclude(provider__exact='') \ - .distinct('provider').values_list('provider', 'provider') - return providers + # def provider_choices(self): + # providers = self.get_queryset().existing().exclude(provider__exact='') \ + # .distinct('provider').values_list('provider', 'provider') + # return providers diff --git a/geotrek/signage/tests/test_views.py b/geotrek/signage/tests/test_views.py index 6548859e03..32489e93ea 100755 --- a/geotrek/signage/tests/test_views.py +++ b/geotrek/signage/tests/test_views.py @@ -14,6 +14,7 @@ from geotrek.authent.tests.factories import PathManagerFactory, StructureFactory from geotrek.common.tests.factories import OrganismFactory from geotrek.signage.models import Signage, Blade +from geotrek.signage.views import SignageList from geotrek.core.tests.factories import PathFactory from geotrek.signage.tests.factories import (SignageFactory, SignageTypeFactory, BladeFactory, BladeTypeFactory, SignageNoPictogramFactory, BladeDirectionFactory, BladeColorFactory, @@ -463,6 +464,26 @@ def test_implantation_year_filter_with_str(self): self.assertIn(i, filter_set.qs) self.assertIn(i2, filter_set.qs) + def test_provider_filter_without_provider(self): + filter_set = SignageFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = SignageFactory.create(provider='my_provider1') + signage2 = SignageFactory.create(provider='my_provider2') + + filter_set = SignageFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) + class BladeFilterSetTest(TestCase): factory = BladeFactory diff --git a/geotrek/tourism/filters.py b/geotrek/tourism/filters.py index 88cb7007ec..7ab4177d23 100644 --- a/geotrek/tourism/filters.py +++ b/geotrek/tourism/filters.py @@ -27,9 +27,13 @@ class TouristicContentFilterSet(ZoningFilterSet, StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=TouristicContent.objects.provider_choices() + choices=(('', '---------'),) ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = TouristicContent.objects.provider_choices() + class Meta(StructureRelatedFilterSet.Meta): model = TouristicContent fields = StructureRelatedFilterSet.Meta.fields + [ @@ -83,9 +87,13 @@ class TouristicEventFilterSet(ZoningFilterSet, StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=TouristicEvent.objects.provider_choices() + choices=(('', '---------'),) ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = TouristicEvent.objects.provider_choices() + class Meta(StructureRelatedFilterSet.Meta): model = TouristicEvent fields = StructureRelatedFilterSet.Meta.fields + [ diff --git a/geotrek/tourism/managers.py b/geotrek/tourism/managers.py index 937b48719c..d4c2e92884 100644 --- a/geotrek/tourism/managers.py +++ b/geotrek/tourism/managers.py @@ -2,7 +2,7 @@ from django.db.models import Q from modeltranslation.manager import MultilingualManager -from geotrek.common.mixins.managers import NoDeleteManager +from geotrek.common.mixins.managers import NoDeleteManager, ProviderChoicesMixin class TouristicContentTypeFilteringManager(MultilingualManager): @@ -61,15 +61,9 @@ def get_queryset(self): return super().get_queryset().filter(in_list=2) -class TouristicContentManager(NoDeleteManager): - def provider_choices(self): - providers = self.get_queryset().existing().exclude(provider__exact='') \ - .distinct('provider').values_list('provider', 'provider') - return providers +class TouristicContentManager(NoDeleteManager, ProviderChoicesMixin): + pass -class TouristicEventManager(NoDeleteManager): - def provider_choices(self): - providers = self.get_queryset().existing().order_by('provider').exclude(provider__exact='') \ - .distinct('provider').values_list('provider', 'provider') - return providers +class TouristicEventManager(NoDeleteManager, ProviderChoicesMixin): + pass diff --git a/geotrek/tourism/tests/test_views.py b/geotrek/tourism/tests/test_views.py index 1aa9c4811a..328f917437 100644 --- a/geotrek/tourism/tests/test_views.py +++ b/geotrek/tourism/tests/test_views.py @@ -30,6 +30,7 @@ TouristicContentType1Factory, TouristicContentType2Factory, TouristicEventFactory) +from geotrek.tourism.filters import TouristicContentFilterSet, TouristicEventFilterSet from geotrek.trekking.tests import factories as trekking_factories from geotrek.trekking.tests.base import TrekkingManagerTest from geotrek.zoning.tests import factories as zoning_factories @@ -566,3 +567,53 @@ def test_geojson(self): self.assertEqual(result['features'][0]['type'], 'Feature') self.assertEqual(result['features'][0]['geometry']['type'], 'Point') self.assertEqual(result['features'][0]['properties']['name'], desk.name) + + +class TouristicContentFilterTest(TestCase): + factory = TouristicContentFactory + filterset = TouristicContentFilterSet + + def test_provider_filter_without_provider(self): + filter_set = TouristicContentFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = TouristicContentFactory.create(provider='my_provider1') + signage2 = TouristicContentFactory.create(provider='my_provider2') + + filter_set = TouristicContentFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) + + +class TouristicEventFilterTest(TestCase): + factory = TouristicEventFactory + filterset = TouristicEventFilterSet + + def test_provider_filter_without_provider(self): + filter_set = TouristicEventFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = TouristicEventFactory.create(provider='my_provider1') + signage2 = TouristicEventFactory.create(provider='my_provider2') + + filter_set = TouristicEventFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) diff --git a/geotrek/trekking/filters.py b/geotrek/trekking/filters.py index bdf58e7d3a..fb7e0031e8 100644 --- a/geotrek/trekking/filters.py +++ b/geotrek/trekking/filters.py @@ -13,7 +13,7 @@ class TrekFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilterSet, Zon field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=Trek.objects.provider_choices() + choices=(('', '---------'),) ) class Meta(StructureRelatedFilterSet.Meta): @@ -24,6 +24,10 @@ class Meta(StructureRelatedFilterSet.Meta): 'source', 'portal', 'reservation_system', 'provider' ] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = Trek.objects.provider_choices() + class POITrekFilter(TopologyFilter): queryset = Trek.objects.existing() @@ -35,9 +39,13 @@ class POIFilterSet(AltimetryPointFilterSet, ValidTopologyFilterSet, ZoningFilter field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=POI.objects.provider_choices() + choices=(('', '---------'),) ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = POI.objects.provider_choices() + class Meta(StructureRelatedFilterSet.Meta): model = POI fields = StructureRelatedFilterSet.Meta.fields + [ @@ -50,9 +58,13 @@ class ServiceFilterSet(AltimetryPointFilterSet, ValidTopologyFilterSet, ZoningFi field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=Service.objects.provider_choices() + choices=(('', '---------'),) ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['provider'].choices = Service.objects.provider_choices() + class Meta(StructureRelatedFilterSet.Meta): model = Service fields = StructureRelatedFilterSet.Meta.fields + ['type', 'provider'] diff --git a/geotrek/trekking/managers.py b/geotrek/trekking/managers.py index 15669a7977..8be2dcfe4b 100644 --- a/geotrek/trekking/managers.py +++ b/geotrek/trekking/managers.py @@ -1,6 +1,6 @@ from django.contrib.gis.db import models -from geotrek.common.mixins.managers import NoDeleteManager +from geotrek.common.mixins.managers import NoDeleteManager, ProviderChoicesMixin from geotrek.core.managers import TopologyManager @@ -14,11 +14,8 @@ def get_queryset(self): return qs.exclude(parent__deleted=True).exclude(child__deleted=True) -class TrekManager(TopologyManager): - def provider_choices(self): - providers = self.get_queryset().existing().order_by('provider').distinct('provider') \ - .exclude(provider__exact='').values_list('provider', 'provider') - return providers +class TrekManager(TopologyManager, ProviderChoicesMixin): + pass class TrekRelationshipManager(models.Manager): @@ -36,15 +33,9 @@ def get_queryset(self): return super().get_queryset().select_related('category') -class POIManager(NoDeleteManager): - def provider_choices(self): - providers = self.get_queryset().existing().exclude(provider__exact='') \ - .distinct('provider').values_list('provider', 'provider') - return providers +class POIManager(NoDeleteManager, ProviderChoicesMixin): + pass -class ServiceManager(NoDeleteManager): - def provider_choices(self): - providers = self.get_queryset().existing().exclude(provider__exact='') \ - .distinct('provider').values_list('provider', 'provider') - return providers +class ServiceManager(NoDeleteManager, ProviderChoicesMixin): + pass diff --git a/geotrek/trekking/tests/test_views.py b/geotrek/trekking/tests/test_views.py index f29bfe0e74..141d78959f 100755 --- a/geotrek/trekking/tests/test_views.py +++ b/geotrek/trekking/tests/test_views.py @@ -37,6 +37,7 @@ # Make sur to register Trek model from geotrek.trekking import urls # NOQA from geotrek.trekking import views as trekking_views +from geotrek.trekking.filters import TrekFilterSet, POIFilterSet, ServiceFilterSet from geotrek.zoning.tests.factories import DistrictFactory, CityFactory from .base import TrekkingManagerTest from .factories import (POIFactory, POITypeFactory, TrekFactory, TrekWithPOIsFactory, @@ -1653,3 +1654,78 @@ def test_delete_infrastructure_refreshes_pdf(self, mock_get): trek.infrastructures[0].delete() trek = Trek.objects.get(pk=self.trek.pk) self.assertFalse(is_file_uptodate(trek.get_map_image_path(), trek.get_date_update())) + + +class TrekFilterTest(TestCase): + factory = TrekFactory + filterset = TrekFilterSet + + def test_provider_filter_without_provider(self): + filter_set = TrekFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = TrekFactory.create(provider='my_provider1') + signage2 = TrekFactory.create(provider='my_provider2') + + filter_set = TrekFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) + + +class POIFilterTest(TestCase): + factory = POIFactory + filterset = POIFilterSet + + def test_provider_filter_without_provider(self): + filter_set = POIFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = POIFactory.create(provider='my_provider1') + signage2 = POIFactory.create(provider='my_provider2') + + filter_set = POIFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) + + +class ServiceFilterTest(TestCase): + factory = ServiceFactory + filterset = ServiceFilterSet + + def test_provider_filter_without_provider(self): + filter_set = ServiceFilterSet(data={}) + filter_form = filter_set.form + + self.assertTrue(filter_form.is_valid()) + self.assertEqual(0, filter_set.qs.count()) + + def test_provider_filter_with_providers(self): + signage1 = ServiceFactory.create(provider='my_provider1') + signage2 = ServiceFactory.create(provider='my_provider2') + + filter_set = ServiceFilterSet() + filter_form = filter_set.form + + self.assertIn('', filter_form.as_p()) + self.assertIn('', filter_form.as_p()) + + self.assertIn(signage1, filter_set.qs) + self.assertIn(signage2, filter_set.qs) From 66210176450285fe0e6dfa93123c2ee86491570e Mon Sep 17 00:00:00 2001 From: Joaquim Nallar Date: Thu, 26 Oct 2023 10:59:25 +0200 Subject: [PATCH 02/12] Release 2.101.3 --- VERSION | 2 +- debian/changelog | 6 +++--- docs/changelog.rst | 4 ++-- docs/conf.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/VERSION b/VERSION index 417519dd85..da249960b4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.101.2+dev +2.101.3 diff --git a/debian/changelog b/debian/changelog index 8bef1dabcd..79ec561b3a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -geotrek-admin (2.101.2+dev) UNRELEASED; urgency=medium +geotrek-admin (2.101.3) RELEASED; urgency=medium - * + * New package release - -- CĂ©lia Prat Tue, 17 Oct 2023 17:15:50 +0200 + -- Joaquim Nallar Thu, 26 Oct 2023 10:57:23 +0200 geotrek-admin (2.101.2) RELEASED; urgency=medium diff --git a/docs/changelog.rst b/docs/changelog.rst index 6e123911d5..9eca942a07 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,13 +2,13 @@ CHANGELOG ========= -2.101.2+dev (XXXX-XX-XX) +2.101.3 (2023-10-17) ------------------------ **Bug fixes** - Fix `sync_rando` admin command failure if Trek has SVG attachment (#3803) - +- Fix provider choices in list filter forms 2.101.2 (2023-10-17) ------------------------ diff --git a/docs/conf.py b/docs/conf.py index 8489898351..1aa4ec8895 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,7 +51,7 @@ # The short X.Y version. version = '2.101' # The full version, including alpha/beta/rc tags. -release = '2.101.2+dev' +release = '2.101.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From f47a87558211b44d02ba95d19f185688a8826e37 Mon Sep 17 00:00:00 2001 From: juggler31 Date: Thu, 26 Oct 2023 11:17:28 +0200 Subject: [PATCH 03/12] Hot fix provider_choices (#3810) Co-authored-by: Jean-Etienne Castagnede --- geotrek/core/tests/test_views.py | 16 +++++++-------- geotrek/infrastructure/tests/test_views.py | 8 ++++---- geotrek/outdoor/tests/test_views.py | 16 +++++++-------- geotrek/sensitivity/tests/test_views.py | 8 ++++---- geotrek/signage/managers.py | 5 ----- geotrek/tourism/tests/test_views.py | 16 +++++++-------- geotrek/trekking/tests/test_views.py | 24 +++++++++++----------- 7 files changed, 44 insertions(+), 49 deletions(-) diff --git a/geotrek/core/tests/test_views.py b/geotrek/core/tests/test_views.py index 3a1c1eb516..5feb5728cf 100644 --- a/geotrek/core/tests/test_views.py +++ b/geotrek/core/tests/test_views.py @@ -888,8 +888,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = PathFactory.create(provider='my_provider1') - signage2 = PathFactory.create(provider='my_provider2') + path1 = PathFactory.create(provider='my_provider1') + path2 = PathFactory.create(provider='my_provider2') filter_set = PathFilterSet() filter_form = filter_set.form @@ -897,8 +897,8 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(path1, filter_set.qs) + self.assertIn(path2, filter_set.qs) class TrailFilterTest(CommonTest, AuthentFixturesTest): @@ -913,8 +913,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = TrailFactory.create(provider='my_provider1') - signage2 = TrailFactory.create(provider='my_provider2') + trail1 = TrailFactory.create(provider='my_provider1') + trail2 = TrailFactory.create(provider='my_provider2') filter_set = TrailFilterSet() filter_form = filter_set.form @@ -922,5 +922,5 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(trail1, filter_set.qs) + self.assertIn(trail2, filter_set.qs) diff --git a/geotrek/infrastructure/tests/test_views.py b/geotrek/infrastructure/tests/test_views.py index e24745e436..b01b336684 100755 --- a/geotrek/infrastructure/tests/test_views.py +++ b/geotrek/infrastructure/tests/test_views.py @@ -131,8 +131,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = InfrastructureFactory.create(provider='my_provider1') - signage2 = InfrastructureFactory.create(provider='my_provider2') + infrastructure1 = InfrastructureFactory.create(provider='my_provider1') + infrastructure2 = InfrastructureFactory.create(provider='my_provider2') filter_set = InfrastructureFilterSet() filter_form = filter_set.form @@ -140,5 +140,5 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(infrastructure1, filter_set.qs) + self.assertIn(infrastructure2, filter_set.qs) diff --git a/geotrek/outdoor/tests/test_views.py b/geotrek/outdoor/tests/test_views.py index a0b060376e..64d0dc78fb 100644 --- a/geotrek/outdoor/tests/test_views.py +++ b/geotrek/outdoor/tests/test_views.py @@ -176,8 +176,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = SiteFactory.create(provider='my_provider1') - signage2 = SiteFactory.create(provider='my_provider2') + site1 = SiteFactory.create(provider='my_provider1') + site2 = SiteFactory.create(provider='my_provider2') filter_set = SiteFilterSet() filter_form = filter_set.form @@ -185,8 +185,8 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(site1, filter_set.qs) + self.assertIn(site2, filter_set.qs) class CourseFilterTest(TestCase): @@ -201,8 +201,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = CourseFactory.create(provider='my_provider1') - signage2 = CourseFactory.create(provider='my_provider2') + course1 = CourseFactory.create(provider='my_provider1') + course2 = CourseFactory.create(provider='my_provider2') filter_set = CourseFilterSet() filter_form = filter_set.form @@ -210,5 +210,5 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(course1, filter_set.qs) + self.assertIn(course2, filter_set.qs) diff --git a/geotrek/sensitivity/tests/test_views.py b/geotrek/sensitivity/tests/test_views.py index d25691374c..e4a7cad644 100644 --- a/geotrek/sensitivity/tests/test_views.py +++ b/geotrek/sensitivity/tests/test_views.py @@ -386,8 +386,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = SensitiveAreaFactory.create(provider='my_provider1') - signage2 = SensitiveAreaFactory.create(provider='my_provider2') + sensitive_area1 = SensitiveAreaFactory.create(provider='my_provider1') + sensitive_area2 = SensitiveAreaFactory.create(provider='my_provider2') filter_set = SensitiveAreaFilterSet() filter_form = filter_set.form @@ -395,5 +395,5 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(sensitive_area1, filter_set.qs) + self.assertIn(sensitive_area2, filter_set.qs) diff --git a/geotrek/signage/managers.py b/geotrek/signage/managers.py index 1bc39e5a37..55d0f7fb17 100644 --- a/geotrek/signage/managers.py +++ b/geotrek/signage/managers.py @@ -8,8 +8,3 @@ def implantation_year_choices(self): .order_by('-implantation_year').distinct('implantation_year') \ .values_list('implantation_year', flat=True) return tuple((value, value) for value in values) - - # def provider_choices(self): - # providers = self.get_queryset().existing().exclude(provider__exact='') \ - # .distinct('provider').values_list('provider', 'provider') - # return providers diff --git a/geotrek/tourism/tests/test_views.py b/geotrek/tourism/tests/test_views.py index 328f917437..86559b244e 100644 --- a/geotrek/tourism/tests/test_views.py +++ b/geotrek/tourism/tests/test_views.py @@ -581,8 +581,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = TouristicContentFactory.create(provider='my_provider1') - signage2 = TouristicContentFactory.create(provider='my_provider2') + touristic_content1 = TouristicContentFactory.create(provider='my_provider1') + touristic_content2 = TouristicContentFactory.create(provider='my_provider2') filter_set = TouristicContentFilterSet() filter_form = filter_set.form @@ -590,8 +590,8 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(touristic_content1, filter_set.qs) + self.assertIn(touristic_content2, filter_set.qs) class TouristicEventFilterTest(TestCase): @@ -606,8 +606,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = TouristicEventFactory.create(provider='my_provider1') - signage2 = TouristicEventFactory.create(provider='my_provider2') + touristic_event1 = TouristicEventFactory.create(provider='my_provider1') + touristic_event2 = TouristicEventFactory.create(provider='my_provider2') filter_set = TouristicEventFilterSet() filter_form = filter_set.form @@ -615,5 +615,5 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(touristic_event1, filter_set.qs) + self.assertIn(touristic_event2, filter_set.qs) diff --git a/geotrek/trekking/tests/test_views.py b/geotrek/trekking/tests/test_views.py index 141d78959f..60c77ce5c3 100755 --- a/geotrek/trekking/tests/test_views.py +++ b/geotrek/trekking/tests/test_views.py @@ -1668,8 +1668,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = TrekFactory.create(provider='my_provider1') - signage2 = TrekFactory.create(provider='my_provider2') + trek1 = TrekFactory.create(provider='my_provider1') + trek2 = TrekFactory.create(provider='my_provider2') filter_set = TrekFilterSet() filter_form = filter_set.form @@ -1677,8 +1677,8 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(trek1, filter_set.qs) + self.assertIn(trek2, filter_set.qs) class POIFilterTest(TestCase): @@ -1693,8 +1693,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = POIFactory.create(provider='my_provider1') - signage2 = POIFactory.create(provider='my_provider2') + poi1 = POIFactory.create(provider='my_provider1') + poi2 = POIFactory.create(provider='my_provider2') filter_set = POIFilterSet() filter_form = filter_set.form @@ -1702,8 +1702,8 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(poi1, filter_set.qs) + self.assertIn(poi2, filter_set.qs) class ServiceFilterTest(TestCase): @@ -1718,8 +1718,8 @@ def test_provider_filter_without_provider(self): self.assertEqual(0, filter_set.qs.count()) def test_provider_filter_with_providers(self): - signage1 = ServiceFactory.create(provider='my_provider1') - signage2 = ServiceFactory.create(provider='my_provider2') + service1 = ServiceFactory.create(provider='my_provider1') + service2 = ServiceFactory.create(provider='my_provider2') filter_set = ServiceFilterSet() filter_form = filter_set.form @@ -1727,5 +1727,5 @@ def test_provider_filter_with_providers(self): self.assertIn('', filter_form.as_p()) self.assertIn('', filter_form.as_p()) - self.assertIn(signage1, filter_set.qs) - self.assertIn(signage2, filter_set.qs) + self.assertIn(service1, filter_set.qs) + self.assertIn(service2, filter_set.qs) From b621b114ace0cf1693a18ec2c95964612f63286e Mon Sep 17 00:00:00 2001 From: Jean-Etienne Castagnede Date: Thu, 26 Oct 2023 11:19:20 +0200 Subject: [PATCH 04/12] fix changelog.rst --- docs/changelog.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9eca942a07..2e4e3129e1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,7 +2,7 @@ CHANGELOG ========= -2.101.3 (2023-10-17) +2.101.3 (2023-10-26) ------------------------ **Bug fixes** @@ -10,7 +10,7 @@ CHANGELOG - Fix `sync_rando` admin command failure if Trek has SVG attachment (#3803) - Fix provider choices in list filter forms -2.101.2 (2023-10-17) +2.101.2 (2023-10-17) ------------------------ **Bug fixes** @@ -19,7 +19,7 @@ CHANGELOG - Fix services list display error (refs ##3795) -2.101.1 (2023-10-06) +2.101.1 (2023-10-06) ------------------------ **Bug fixes** @@ -27,7 +27,7 @@ CHANGELOG - POI cirkwi XML endpoint is fixed (2.101.0 regression) (#3783) -2.101.0 (2023-10-05) +2.101.0 (2023-10-05) ------------------------ **New features** @@ -58,7 +58,7 @@ CHANGELOG - Update WYSIWYG link to help user when creating labels -2.100.2 (2023-09-12) +2.100.2 (2023-09-12) ------------------------ **Improvements** @@ -83,7 +83,7 @@ CHANGELOG - Increase length size of label on TouristicEventOrganizer model to fix migrations problems (#3719) -2.100.1 (2023-09-05) +2.100.1 (2023-09-05) ------------------------- **Documentation** @@ -99,7 +99,7 @@ CHANGELOG - Remove unused folder 'bulkimport' from project (#3673) -2.100.0 (2023-09-05) +2.100.0 (2023-09-05) ----------------------- **DO NOT USE** @@ -138,7 +138,7 @@ CHANGELOG - Upgrade `django-mapentity` -2.99.0 (2023-07-18) +2.99.0 (2023-07-18) ----------------------- **New features** @@ -166,7 +166,7 @@ CHANGELOG - Upgrade `django-mapentity` -2.98.1 (2023-05-30) +2.98.1 (2023-05-30) ----------------------- **Bug fixes** From e8112b2d72756312c6c5a3d290e9ee58f46689f7 Mon Sep 17 00:00:00 2001 From: Jean-Etienne Castagnede Date: Thu, 26 Oct 2023 11:20:52 +0200 Subject: [PATCH 05/12] Update test_views.py --- geotrek/signage/tests/test_views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/geotrek/signage/tests/test_views.py b/geotrek/signage/tests/test_views.py index 32489e93ea..9f826f9f95 100755 --- a/geotrek/signage/tests/test_views.py +++ b/geotrek/signage/tests/test_views.py @@ -14,7 +14,6 @@ from geotrek.authent.tests.factories import PathManagerFactory, StructureFactory from geotrek.common.tests.factories import OrganismFactory from geotrek.signage.models import Signage, Blade -from geotrek.signage.views import SignageList from geotrek.core.tests.factories import PathFactory from geotrek.signage.tests.factories import (SignageFactory, SignageTypeFactory, BladeFactory, BladeTypeFactory, SignageNoPictogramFactory, BladeDirectionFactory, BladeColorFactory, From ad2e9aa434d83302e9bd6d729c5b8603adc66099 Mon Sep 17 00:00:00 2001 From: Joaquim Nallar Date: Thu, 26 Oct 2023 12:07:22 +0200 Subject: [PATCH 06/12] Back to development --- VERSION | 2 +- debian/changelog | 6 ++++++ docs/changelog.rst | 3 +++ docs/conf.py | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index da249960b4..289335a6b0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.101.3 +2.101.3+dev diff --git a/debian/changelog b/debian/changelog index 79ec561b3a..32146d93d5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +geotrek-admin (2.101.3+dev) UNRELEASED; urgency=medium + + * + + -- Joaquim Nallar Thu, 26 Oct 2023 12:04:29 +0200 + geotrek-admin (2.101.3) RELEASED; urgency=medium * New package release diff --git a/docs/changelog.rst b/docs/changelog.rst index 2e4e3129e1..15b3615246 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,9 @@ CHANGELOG ========= +2.101.3+dev (XXXX-XX-XX) +------------------------ + 2.101.3 (2023-10-26) ------------------------ diff --git a/docs/conf.py b/docs/conf.py index 1aa4ec8895..cc981b1c0b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,7 +51,7 @@ # The short X.Y version. version = '2.101' # The full version, including alpha/beta/rc tags. -release = '2.101.3' +release = '2.101.3+dev' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 93d7095df0b6fbec03c01ca1cfb401e48e131948 Mon Sep 17 00:00:00 2001 From: J-E Castagnede Date: Thu, 26 Oct 2023 16:35:56 +0200 Subject: [PATCH 07/12] fix and improve filter choices --- geotrek/infrastructure/filters.py | 3 ++- geotrek/maintenance/filters.py | 12 +++++++++-- geotrek/maintenance/managers.py | 5 +++-- geotrek/signage/filters.py | 3 ++- geotrek/tourism/filters.py | 18 ++-------------- geotrek/tourism/tests/test_filters.py | 31 +++++++++++++++++---------- 6 files changed, 39 insertions(+), 33 deletions(-) diff --git a/geotrek/infrastructure/filters.py b/geotrek/infrastructure/filters.py index 1a88cc1e33..29379899cc 100644 --- a/geotrek/infrastructure/filters.py +++ b/geotrek/infrastructure/filters.py @@ -14,7 +14,7 @@ class InfrastructureFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilt description = CharFilter(label=_('Description'), lookup_expr='icontains') implantation_year = MultipleChoiceFilter(choices=(('', '---------'),)) intervention_year = MultipleChoiceFilter(label=_("Intervention year"), method='filter_intervention_year', - choices=Intervention.objects.year_choices()) + choices=(('', '---------'),)) category = MultipleChoiceFilter(label=_("Category"), field_name='type__type', choices=INFRASTRUCTURE_TYPES) trail = TopologyFilterTrail(label=_('Trail'), required=False) @@ -44,3 +44,4 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.form.fields['implantation_year'].choices = Infrastructure.objects.implantation_year_choices() self.form.fields['provider'].choices = Infrastructure.objects.provider_choices() + self.form.fields['intervention_year'].choices = Intervention.objects.year_choices() diff --git a/geotrek/maintenance/filters.py b/geotrek/maintenance/filters.py index 852a1858b3..a6a5a293c5 100644 --- a/geotrek/maintenance/filters.py +++ b/geotrek/maintenance/filters.py @@ -130,7 +130,7 @@ class InterventionFilterSet(AltimetryInterventionFilterSet, ZoningFilterSet, Str ON_CHOICES += (('course', _("Outdoor Course")), ('site', _("Outdoor Site")),) bbox = PolygonTopologyFilter(lookup_expr='intersects') - year = MultipleChoiceFilter(choices=Intervention.objects.year_choices(), + year = MultipleChoiceFilter(choices=(('', '---------'),), field_name='date', lookup_expr='year', label=_("Year")) on = ChoiceFilter(field_name='target_type__model', choices=ON_CHOICES, label=_("On"), empty_label=_("On")) area_type = InterventionIntersectionFilterRestrictedAreaType(label=_('Restricted area type'), required=False, @@ -146,12 +146,16 @@ class Meta(StructureRelatedFilterSet.Meta): 'status', 'type', 'stake', 'subcontracting', 'project', 'on', ] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['year'].choices = Intervention.objects.year_choices() + class ProjectFilterSet(StructureRelatedFilterSet): bbox = PythonPolygonFilter(field_name='geom') year = MultipleChoiceFilter( label=_("Year of activity"), method='filter_year', - choices=lambda: Project.objects.year_choices() # Could change over time + choices=(('', '---------'),) ) city = ProjectIntersectionFilterCity(label=_('City'), lookup_expr='intersects', required=False) district = ProjectIntersectionFilterDistrict(label=_('District'), lookup_expr='intersects', required=False) @@ -170,3 +174,7 @@ def filter_year(self, qs, name, values): for value in values: q |= Q(begin_year__lte=value, end_year__gte=value) return qs.filter(q) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['year'].choices = Project.objects.year_choices() diff --git a/geotrek/maintenance/managers.py b/geotrek/maintenance/managers.py index 0cb2a00c15..2132bae747 100644 --- a/geotrek/maintenance/managers.py +++ b/geotrek/maintenance/managers.py @@ -6,8 +6,9 @@ class InterventionManager(NoDeleteManager): def year_choices(self): - return self.existing().filter(date__isnull=False).annotate(year=ExtractYear('date')) \ - .order_by('-year').distinct().values_list('year', 'year') + values = self.existing().filter(date__isnull=False).annotate(year=ExtractYear('date')) \ + .order_by('-year').distinct().values_list('year', flat=True) + return [(year, year) for year in values] class ProjectManager(NoDeleteManager): diff --git a/geotrek/signage/filters.py b/geotrek/signage/filters.py index 431360027f..5ea8090145 100644 --- a/geotrek/signage/filters.py +++ b/geotrek/signage/filters.py @@ -29,7 +29,7 @@ class SignageFilterSet(AltimetryPointFilterSet, ValidTopologyFilterSet, ZoningFi description = CharFilter(label=_('Description'), lookup_expr='icontains') implantation_year = MultipleChoiceFilter(choices=(('', '---------'),)) intervention_year = MultipleChoiceFilter(label=_("Intervention year"), method='filter_intervention_year', - choices=Intervention.objects.year_choices()) + choices=(('', '---------'),)) trail = TopologyFilterTrail(label=_('Trail'), required=False) provider = ChoiceFilter( field_name='provider', @@ -48,6 +48,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.form.fields['implantation_year'].choices = Signage.objects.implantation_year_choices() self.form.fields['provider'].choices = Signage.objects.provider_choices() + self.form.fields['intervention_year'].choices = Intervention.objects.year_choices() def filter_intervention_year(self, qs, name, value): signage_ct = ContentType.objects.get_for_model(Signage) diff --git a/geotrek/tourism/filters.py b/geotrek/tourism/filters.py index 7ab4177d23..8114f93c71 100644 --- a/geotrek/tourism/filters.py +++ b/geotrek/tourism/filters.py @@ -43,20 +43,6 @@ class Meta(StructureRelatedFilterSet.Meta): ] -class AfterFilter(django_filters.DateFilter): - def filter(self, qs, value): - if not value: - return qs - return qs.filter(end_date__gte=value) - - -class BeforeFilter(django_filters.DateFilter): - def filter(self, qs, value): - if not value: - return qs - return qs.filter(begin_date__lte=value) - - class CompletedFilter(django_filters.BooleanFilter): """ Filter events with end_date in past (event completed) @@ -80,8 +66,8 @@ def filter(self, qs, value): class TouristicEventFilterSet(ZoningFilterSet, StructureRelatedFilterSet): - after = AfterFilter(label=_("After")) - before = BeforeFilter(label=_("Before")) + after = django_filters.DateFilter(label=_("After"), lookup_expr='gte', field_name='end_date') + before = django_filters.DateFilter(label=_("Before"), lookup_expr='lte', field_name='begin_date') completed = CompletedFilter(label=_("Completed")) provider = ChoiceFilter( field_name='provider', diff --git a/geotrek/tourism/tests/test_filters.py b/geotrek/tourism/tests/test_filters.py index 4e60b5db51..ab9c6520fa 100644 --- a/geotrek/tourism/tests/test_filters.py +++ b/geotrek/tourism/tests/test_filters.py @@ -2,10 +2,12 @@ from geotrek.tourism.tests.factories import TouristicEventFactory from geotrek.tourism.models import TouristicEvent -from geotrek.tourism.filters import CompletedFilter, BeforeFilter, AfterFilter +from geotrek.tourism.filters import CompletedFilter, TouristicEventFilterSet -class FilterList(TestCase): +class TouristicEventFilterSetTestCase(TestCase): + filter_class = TouristicEventFilterSet + @classmethod def setUpTestData(cls): # TouristicEvent : end_date = datetime.today() @@ -14,17 +16,24 @@ def setUpTestData(cls): def test_touristicevent_filter_completed(self): cf = CompletedFilter() - bf = BeforeFilter() - af = AfterFilter() # date <= Today (True) : completed # date >= Today (False) : not completed self.assertEqual(cf.filter(self.qs, False).count(), 1) self.assertEqual(cf.filter(self.qs, True).count(), 0) + + def test_before_filter(self): + filter = self.filter_class(data={'before': '2000-01-01'}) # Before : date <= date_of_filter - self.assertEqual(bf.filter(self.qs, '2000-01-01').count(), 0) - self.assertEqual(bf.filter(self.qs, '2150-01-01').count(), 1) - self.assertEqual(bf.filter(self.qs, '2300-01-01').count(), 1) - # After : date >= date_of_filter - self.assertEqual(af.filter(self.qs, '2000-01-01').count(), 1) - self.assertEqual(af.filter(self.qs, '2150-01-01').count(), 1) - self.assertEqual(af.filter(self.qs, '2300-01-01').count(), 0) + self.assertEqual(filter.qs.count(), 0) + filter = self.filter_class(data={'before': '2150-01-01'}) + self.assertEqual(filter.qs.count(), 1) + filter = self.filter_class(data={'before': '2300-01-01'}) + self.assertEqual(filter.qs.count(), 1) + + def test_after_filter(self): + filter = self.filter_class(data={'after': '2000-01-01'}) + self.assertEqual(filter.qs.count(), 1) + filter = self.filter_class(data={'after': '2150-01-01'}) + self.assertEqual(filter.qs.count(), 1) + filter = self.filter_class(data={'after': '2300-01-01'}) + self.assertEqual(filter.qs.count(), 0) From affee66bc2d1a8ed06459dc8ad90a44adc084f7b Mon Sep 17 00:00:00 2001 From: J-E Castagnede Date: Thu, 26 Oct 2023 17:08:53 +0200 Subject: [PATCH 08/12] update changelog.rst --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 15b3615246..5357568ad8 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,11 @@ CHANGELOG 2.101.3+dev (XXXX-XX-XX) ------------------------ +**Bug fixes** + +- Fix filters tigger exception with data and not updated until application restart (#3812) + + 2.101.3 (2023-10-26) ------------------------ From a97e6d2a3e4a17b2efb7c7e9dece5d14701ee1e7 Mon Sep 17 00:00:00 2001 From: Jean-Etienne Castagnede Date: Fri, 27 Oct 2023 09:39:27 +0200 Subject: [PATCH 09/12] Update docs/changelog.rst --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 5357568ad8..76df434cea 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,7 +7,7 @@ CHANGELOG **Bug fixes** -- Fix filters tigger exception with data and not updated until application restart (#3812) +- Fix: filters choices can raise exception in lists and not updated until application restart (#3812) 2.101.3 (2023-10-26) From 7d8fa19585e2b3a5cf0bf78a8aa9d40a45d11ed5 Mon Sep 17 00:00:00 2001 From: J-E Castagnede Date: Fri, 27 Oct 2023 10:06:10 +0200 Subject: [PATCH 10/12] clean and refacto filters --- geotrek/cirkwi/filters.py | 21 ++----------------- geotrek/common/fields.py | 7 ------- .../{filters.py => filters/__init__.py} | 5 ++++- geotrek/common/filters/fields.py | 20 ++++++++++++++++++ geotrek/common/mixins/filters.py | 0 geotrek/core/filters.py | 12 ++--------- geotrek/infrastructure/filters.py | 12 +++-------- geotrek/maintenance/filters.py | 12 ++--------- geotrek/outdoor/filters.py | 12 ++--------- geotrek/sensitivity/filters.py | 2 +- geotrek/signage/filters.py | 12 +++-------- geotrek/tourism/filters.py | 12 ++--------- geotrek/trekking/filters.py | 18 +++------------- 13 files changed, 44 insertions(+), 101 deletions(-) delete mode 100644 geotrek/common/fields.py rename geotrek/common/{filters.py => filters/__init__.py} (83%) create mode 100644 geotrek/common/filters/fields.py create mode 100644 geotrek/common/mixins/filters.py diff --git a/geotrek/cirkwi/filters.py b/geotrek/cirkwi/filters.py index 3d4447e42b..81e55a4040 100644 --- a/geotrek/cirkwi/filters.py +++ b/geotrek/cirkwi/filters.py @@ -1,25 +1,8 @@ -from django_filters import ModelMultipleChoiceFilter, FilterSet -from django_filters.fields import ModelChoiceField +from django_filters import FilterSet from geotrek.authent.models import Structure +from geotrek.common.filters import ComaSeparatedMultipleModelChoiceFilter from geotrek.common.models import TargetPortal from geotrek.trekking.models import POI, Trek -from django.forms import ValidationError - - -class ComaSeparatedMultipleModelChoiceField(ModelChoiceField): - def to_python(self, value): - if value in self.empty_values: - return None - try: - key = self.to_field_name or 'pk' - value = self.queryset.filter(**{f'{key}__in': value.split(',')}) - except (ValueError, TypeError): - raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice') - return value - - -class ComaSeparatedMultipleModelChoiceFilter(ModelMultipleChoiceFilter): - field_class = ComaSeparatedMultipleModelChoiceField class CirkwiPOIFilterSet(FilterSet): diff --git a/geotrek/common/fields.py b/geotrek/common/fields.py deleted file mode 100644 index 26a18224cf..0000000000 --- a/geotrek/common/fields.py +++ /dev/null @@ -1,7 +0,0 @@ -from django_filters.fields import RangeField - -from .widgets import OneLineRangeWidget - - -class OneLineRangeField(RangeField): - widget = OneLineRangeWidget(attrs={'class': 'minmax-field'}) diff --git a/geotrek/common/filters.py b/geotrek/common/filters/__init__.py similarity index 83% rename from geotrek/common/filters.py rename to geotrek/common/filters/__init__.py index 78c5a0bc8f..41801bdb94 100644 --- a/geotrek/common/filters.py +++ b/geotrek/common/filters/__init__.py @@ -2,9 +2,12 @@ from django_filters import ModelMultipleChoiceFilter, RangeFilter from mapentity.filters import MapEntityFilterSet +from geotrek.common.filters.fields import ComaSeparatedMultipleModelChoiceField, OneLineRangeField from geotrek.common.models import HDViewPoint -from .fields import OneLineRangeField + +class ComaSeparatedMultipleModelChoiceFilter(ModelMultipleChoiceFilter): + field_class = ComaSeparatedMultipleModelChoiceField class OptionalRangeFilter(RangeFilter): diff --git a/geotrek/common/filters/fields.py b/geotrek/common/filters/fields.py new file mode 100644 index 0000000000..59bf64115f --- /dev/null +++ b/geotrek/common/filters/fields.py @@ -0,0 +1,20 @@ +from django.core.exceptions import ValidationError +from django_filters.fields import ModelChoiceField, RangeField + +from geotrek.common.widgets import OneLineRangeWidget + + +class ComaSeparatedMultipleModelChoiceField(ModelChoiceField): + def to_python(self, value): + if value in self.empty_values: + return None + try: + key = self.to_field_name or 'pk' + value = self.queryset.filter(**{f'{key}__in': value.split(',')}) + except (ValueError, TypeError): + raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice') + return value + + +class OneLineRangeField(RangeField): + widget = OneLineRangeWidget(attrs={'class': 'minmax-field'}) diff --git a/geotrek/common/mixins/filters.py b/geotrek/common/mixins/filters.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/geotrek/core/filters.py b/geotrek/core/filters.py index 9a2e941c4f..ffbdd64e39 100644 --- a/geotrek/core/filters.py +++ b/geotrek/core/filters.py @@ -103,7 +103,7 @@ class PathFilterSet(AltimetryAllGeometriesFilterSet, ZoningFilterSet, StructureR field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: Path.objects.provider_choices() ) networks = ModelMultipleChoiceFilter(queryset=Network.objects.all().select_related("structure")) usages = ModelMultipleChoiceFilter(queryset=Usage.objects.all().select_related("structure")) @@ -114,10 +114,6 @@ class Meta(StructureRelatedFilterSet.Meta): fields = StructureRelatedFilterSet.Meta.fields + \ ['valid', 'networks', 'usages', 'comfort', 'stake', 'draft', 'provider'] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['provider'].choices = Path.objects.provider_choices() - class TrailFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilterSet, ZoningFilterSet, StructureRelatedFilterSet): """Trail filter set""" @@ -134,7 +130,7 @@ class TrailFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilterSet, Zo field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: Trail.objects.provider_choices() ) class Meta(StructureRelatedFilterSet.Meta): @@ -142,10 +138,6 @@ class Meta(StructureRelatedFilterSet.Meta): fields = StructureRelatedFilterSet.Meta.fields + \ ['name', 'category', 'departure', 'arrival', 'certification_labels', 'comments', 'provider'] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['provider'].choices = Trail.objects.provider_choices() - class TopologyFilterTrail(TopologyFilter): queryset = Trail.objects.existing() diff --git a/geotrek/infrastructure/filters.py b/geotrek/infrastructure/filters.py index 29379899cc..e397777700 100644 --- a/geotrek/infrastructure/filters.py +++ b/geotrek/infrastructure/filters.py @@ -12,9 +12,9 @@ class InfrastructureFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilterSet, ZoningFilterSet, StructureRelatedFilterSet): name = CharFilter(label=_('Name'), lookup_expr='icontains') description = CharFilter(label=_('Description'), lookup_expr='icontains') - implantation_year = MultipleChoiceFilter(choices=(('', '---------'),)) + implantation_year = MultipleChoiceFilter(choices=lambda: Infrastructure.objects.implantation_year_choices()) intervention_year = MultipleChoiceFilter(label=_("Intervention year"), method='filter_intervention_year', - choices=(('', '---------'),)) + choices=lambda: Intervention.objects.year_choices()) category = MultipleChoiceFilter(label=_("Category"), field_name='type__type', choices=INFRASTRUCTURE_TYPES) trail = TopologyFilterTrail(label=_('Trail'), required=False) @@ -24,7 +24,7 @@ class InfrastructureFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilt field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: Infrastructure.objects.provider_choices() ) class Meta(StructureRelatedFilterSet.Meta): @@ -39,9 +39,3 @@ def filter_intervention_year(self, qs, name, value): interventions = Intervention.objects.filter(target_type=infrastructure_ct, date__year__in=value) \ .values_list('target_id', flat=True) return qs.filter(id__in=interventions).distinct() - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['implantation_year'].choices = Infrastructure.objects.implantation_year_choices() - self.form.fields['provider'].choices = Infrastructure.objects.provider_choices() - self.form.fields['intervention_year'].choices = Intervention.objects.year_choices() diff --git a/geotrek/maintenance/filters.py b/geotrek/maintenance/filters.py index a6a5a293c5..041bbbc2f8 100644 --- a/geotrek/maintenance/filters.py +++ b/geotrek/maintenance/filters.py @@ -130,7 +130,7 @@ class InterventionFilterSet(AltimetryInterventionFilterSet, ZoningFilterSet, Str ON_CHOICES += (('course', _("Outdoor Course")), ('site', _("Outdoor Site")),) bbox = PolygonTopologyFilter(lookup_expr='intersects') - year = MultipleChoiceFilter(choices=(('', '---------'),), + year = MultipleChoiceFilter(choices=lambda: Intervention.objects.year_choices(), field_name='date', lookup_expr='year', label=_("Year")) on = ChoiceFilter(field_name='target_type__model', choices=ON_CHOICES, label=_("On"), empty_label=_("On")) area_type = InterventionIntersectionFilterRestrictedAreaType(label=_('Restricted area type'), required=False, @@ -146,16 +146,12 @@ class Meta(StructureRelatedFilterSet.Meta): 'status', 'type', 'stake', 'subcontracting', 'project', 'on', ] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['year'].choices = Intervention.objects.year_choices() - class ProjectFilterSet(StructureRelatedFilterSet): bbox = PythonPolygonFilter(field_name='geom') year = MultipleChoiceFilter( label=_("Year of activity"), method='filter_year', - choices=(('', '---------'),) + choices=lambda: Project.objects.year_choices() ) city = ProjectIntersectionFilterCity(label=_('City'), lookup_expr='intersects', required=False) district = ProjectIntersectionFilterDistrict(label=_('District'), lookup_expr='intersects', required=False) @@ -174,7 +170,3 @@ def filter_year(self, qs, name, values): for value in values: q |= Q(begin_year__lte=value, end_year__gte=value) return qs.filter(q) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['year'].choices = Project.objects.year_choices() diff --git a/geotrek/outdoor/filters.py b/geotrek/outdoor/filters.py index ab10c540e3..75984020b9 100644 --- a/geotrek/outdoor/filters.py +++ b/geotrek/outdoor/filters.py @@ -17,7 +17,7 @@ class SiteFilterSet(ZoningFilterSet, StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: Site.objects.provider_choices() ) class Meta(StructureRelatedFilterSet.Meta): @@ -27,10 +27,6 @@ class Meta(StructureRelatedFilterSet.Meta): 'web_links', 'type', 'orientation', 'wind', 'provider' ] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['provider'].choices = Site.objects.provider_choices() - def filter_orientation(self, qs, name, values): q = Q() for value in values: @@ -62,7 +58,7 @@ class CourseFilterSet(ZoningFilterSet, StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: Course.objects.provider_choices() ) class Meta(StructureRelatedFilterSet.Meta): @@ -73,10 +69,6 @@ class Meta(StructureRelatedFilterSet.Meta): 'height', 'provider' ] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['provider'].choices = Course.objects.provider_choices() - def filter_orientation(self, qs, name, values): q = Q() for value in values: diff --git a/geotrek/sensitivity/filters.py b/geotrek/sensitivity/filters.py index 27f277936d..449442be4f 100644 --- a/geotrek/sensitivity/filters.py +++ b/geotrek/sensitivity/filters.py @@ -13,7 +13,7 @@ class SensitiveAreaFilterSet(StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: SensitiveArea.objects.provider_choices() ) def __init__(self, *args, **kwargs): diff --git a/geotrek/signage/filters.py b/geotrek/signage/filters.py index 5ea8090145..26b4cb61ef 100644 --- a/geotrek/signage/filters.py +++ b/geotrek/signage/filters.py @@ -27,15 +27,15 @@ def filter(self, qs, value): class SignageFilterSet(AltimetryPointFilterSet, ValidTopologyFilterSet, ZoningFilterSet, StructureRelatedFilterSet): name = CharFilter(label=_('Name'), lookup_expr='icontains') description = CharFilter(label=_('Description'), lookup_expr='icontains') - implantation_year = MultipleChoiceFilter(choices=(('', '---------'),)) + implantation_year = MultipleChoiceFilter(choices=lambda: Signage.objects.implantation_year_choices()) intervention_year = MultipleChoiceFilter(label=_("Intervention year"), method='filter_intervention_year', - choices=(('', '---------'),)) + choices=lambda: Intervention.objects.year_choices()) trail = TopologyFilterTrail(label=_('Trail'), required=False) provider = ChoiceFilter( field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: Signage.objects.provider_choices() ) class Meta(StructureRelatedFilterSet.Meta): @@ -44,12 +44,6 @@ class Meta(StructureRelatedFilterSet.Meta): 'published', 'code', 'printed_elevation', 'manager', 'sealing', 'access', 'provider'] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['implantation_year'].choices = Signage.objects.implantation_year_choices() - self.form.fields['provider'].choices = Signage.objects.provider_choices() - self.form.fields['intervention_year'].choices = Intervention.objects.year_choices() - def filter_intervention_year(self, qs, name, value): signage_ct = ContentType.objects.get_for_model(Signage) interventions = Intervention.objects.filter(target_type=signage_ct, date__year__in=value) \ diff --git a/geotrek/tourism/filters.py b/geotrek/tourism/filters.py index 8114f93c71..b59c0bb90f 100644 --- a/geotrek/tourism/filters.py +++ b/geotrek/tourism/filters.py @@ -27,13 +27,9 @@ class TouristicContentFilterSet(ZoningFilterSet, StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: TouristicContent.objects.provider_choices() ) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['provider'].choices = TouristicContent.objects.provider_choices() - class Meta(StructureRelatedFilterSet.Meta): model = TouristicContent fields = StructureRelatedFilterSet.Meta.fields + [ @@ -73,13 +69,9 @@ class TouristicEventFilterSet(ZoningFilterSet, StructureRelatedFilterSet): field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: TouristicEvent.objects.provider_choices() ) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['provider'].choices = TouristicEvent.objects.provider_choices() - class Meta(StructureRelatedFilterSet.Meta): model = TouristicEvent fields = StructureRelatedFilterSet.Meta.fields + [ diff --git a/geotrek/trekking/filters.py b/geotrek/trekking/filters.py index fb7e0031e8..4f6ded6ca2 100644 --- a/geotrek/trekking/filters.py +++ b/geotrek/trekking/filters.py @@ -13,7 +13,7 @@ class TrekFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilterSet, Zon field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: Trek.objects.provider_choices() ) class Meta(StructureRelatedFilterSet.Meta): @@ -24,10 +24,6 @@ class Meta(StructureRelatedFilterSet.Meta): 'source', 'portal', 'reservation_system', 'provider' ] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['provider'].choices = Trek.objects.provider_choices() - class POITrekFilter(TopologyFilter): queryset = Trek.objects.existing() @@ -39,13 +35,9 @@ class POIFilterSet(AltimetryPointFilterSet, ValidTopologyFilterSet, ZoningFilter field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: POI.objects.provider_choices() ) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['provider'].choices = POI.objects.provider_choices() - class Meta(StructureRelatedFilterSet.Meta): model = POI fields = StructureRelatedFilterSet.Meta.fields + [ @@ -58,13 +50,9 @@ class ServiceFilterSet(AltimetryPointFilterSet, ValidTopologyFilterSet, ZoningFi field_name='provider', empty_label=_("Provider"), label=_("Provider"), - choices=(('', '---------'),) + choices=lambda: Service.objects.provider_choices() ) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form.fields['provider'].choices = Service.objects.provider_choices() - class Meta(StructureRelatedFilterSet.Meta): model = Service fields = StructureRelatedFilterSet.Meta.fields + ['type', 'provider'] From 935e6d23e905daf2a7332fd88f9ef785b932626a Mon Sep 17 00:00:00 2001 From: J-E Castagnede Date: Fri, 27 Oct 2023 11:37:52 +0200 Subject: [PATCH 11/12] fix tests --- geotrek/core/tests/test_views.py | 16 +++++++--------- geotrek/tourism/tests/test_functional.py | 2 +- geotrek/trekking/tests/test_views.py | 8 ++++---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/geotrek/core/tests/test_views.py b/geotrek/core/tests/test_views.py index 5feb5728cf..53b850eab3 100644 --- a/geotrek/core/tests/test_views.py +++ b/geotrek/core/tests/test_views.py @@ -569,8 +569,7 @@ def test_draft_path_layer_cache(self): obj = self.modelfactory(draft=False) self.modelfactory(draft=True) - # There are 7 queries to get layer without drafts - with self.assertNumQueries(5): + with self.assertNumQueries(4): response = self.client.get(obj.get_layer_url(), {"_no_draft": "true"}) self.assertEqual(len(response.json()['features']), 1) @@ -599,7 +598,7 @@ def test_draft_path_layer_cache(self): self.modelfactory(draft=False) # Cache was updated, the path was not a draft : we get 7 queries - with self.assertNumQueries(5): + with self.assertNumQueries(4): self.client.get(obj.get_layer_url(), {"_no_draft": "true"}) def test_path_layer_cache(self): @@ -612,8 +611,7 @@ def test_path_layer_cache(self): obj = self.modelfactory(draft=False) self.modelfactory(draft=True) - # There are 7 queries to get layer without drafts - with self.assertNumQueries(5): + with self.assertNumQueries(4): response = self.client.get(obj.get_layer_url()) self.assertEqual(len(response.json()['features']), 2) @@ -636,13 +634,13 @@ def test_path_layer_cache(self): self.modelfactory(draft=True) # Cache is updated when we add a draft path - with self.assertNumQueries(5): + with self.assertNumQueries(4): self.client.get(obj.get_layer_url()) self.modelfactory(draft=False) # Cache is updated when we add a path - with self.assertNumQueries(5): + with self.assertNumQueries(4): self.client.get(obj.get_layer_url()) @@ -695,7 +693,7 @@ def test_denormalized_path_trails(self): PathFactory.create_batch(size=50) TrailFactory.create_batch(size=50) self.login() - with self.assertNumQueries(6): + with self.assertNumQueries(5): self.client.get(reverse('core:path-drf-list', kwargs={'format': 'datatables'})) @@ -799,7 +797,7 @@ def test_add_trail_from_existing_topology(self): def test_perfs_export_csv(self): self.modelfactory.create() - with self.assertNumQueries(14): + with self.assertNumQueries(11): self.client.get(self.model.get_format_list_url() + '?format=csv') diff --git a/geotrek/tourism/tests/test_functional.py b/geotrek/tourism/tests/test_functional.py index 724679a800..cdc517a28b 100644 --- a/geotrek/tourism/tests/test_functional.py +++ b/geotrek/tourism/tests/test_functional.py @@ -368,7 +368,7 @@ def test_csv_participants_count(self): total_count = sum(map(attrgetter('count'), counts)) self.assertEqual(event.participants_total, total_count) self.assertEqual(event.participants_total_verbose_name, "Number of participants") - with self.assertNumQueries(18): + with self.assertNumQueries(15): response = self.client.get(event.get_format_list_url()) self.assertEqual(response.status_code, 200) self.assertEqual(response.get('Content-Type'), 'text/csv') diff --git a/geotrek/trekking/tests/test_views.py b/geotrek/trekking/tests/test_views.py index 60c77ce5c3..1f505e7870 100755 --- a/geotrek/trekking/tests/test_views.py +++ b/geotrek/trekking/tests/test_views.py @@ -182,10 +182,10 @@ def test_listing_number_queries(self): self.modelfactory.build_batch(1000) DistrictFactory.build_batch(10) - with self.assertNumQueries(6): + with self.assertNumQueries(5): self.client.get(self.model.get_datatablelist_url()) - with self.assertNumQueries(10): + with self.assertNumQueries(8): self.client.get(self.model.get_format_list_url()) def test_list_in_csv(self): @@ -1515,10 +1515,10 @@ def test_listing_number_queries(self): self.modelfactory.build_batch(1000) DistrictFactory.build_batch(10) - with self.assertNumQueries(6): + with self.assertNumQueries(5): self.client.get(self.model.get_datatablelist_url()) - with self.assertNumQueries(7): + with self.assertNumQueries(4): self.client.get(self.model.get_format_list_url()) def test_services_on_treks_do_not_exist(self): From 26d1e8e00a9443705aef792078a7ffafd48bfb21 Mon Sep 17 00:00:00 2001 From: Jean-Etienne Castagnede Date: Fri, 27 Oct 2023 11:42:55 +0200 Subject: [PATCH 12/12] Fix and improve filter choices (#3812) (#3811) * fix and improve filter choices * update changelog.rst * Update docs/changelog.rst --- docs/changelog.rst | 5 +++++ geotrek/infrastructure/filters.py | 3 ++- geotrek/maintenance/filters.py | 12 +++++++++-- geotrek/maintenance/managers.py | 5 +++-- geotrek/signage/filters.py | 3 ++- geotrek/tourism/filters.py | 18 ++-------------- geotrek/tourism/tests/test_filters.py | 31 +++++++++++++++++---------- 7 files changed, 44 insertions(+), 33 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 15b3615246..76df434cea 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,11 @@ CHANGELOG 2.101.3+dev (XXXX-XX-XX) ------------------------ +**Bug fixes** + +- Fix: filters choices can raise exception in lists and not updated until application restart (#3812) + + 2.101.3 (2023-10-26) ------------------------ diff --git a/geotrek/infrastructure/filters.py b/geotrek/infrastructure/filters.py index 1a88cc1e33..29379899cc 100644 --- a/geotrek/infrastructure/filters.py +++ b/geotrek/infrastructure/filters.py @@ -14,7 +14,7 @@ class InfrastructureFilterSet(AltimetryAllGeometriesFilterSet, ValidTopologyFilt description = CharFilter(label=_('Description'), lookup_expr='icontains') implantation_year = MultipleChoiceFilter(choices=(('', '---------'),)) intervention_year = MultipleChoiceFilter(label=_("Intervention year"), method='filter_intervention_year', - choices=Intervention.objects.year_choices()) + choices=(('', '---------'),)) category = MultipleChoiceFilter(label=_("Category"), field_name='type__type', choices=INFRASTRUCTURE_TYPES) trail = TopologyFilterTrail(label=_('Trail'), required=False) @@ -44,3 +44,4 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.form.fields['implantation_year'].choices = Infrastructure.objects.implantation_year_choices() self.form.fields['provider'].choices = Infrastructure.objects.provider_choices() + self.form.fields['intervention_year'].choices = Intervention.objects.year_choices() diff --git a/geotrek/maintenance/filters.py b/geotrek/maintenance/filters.py index 852a1858b3..a6a5a293c5 100644 --- a/geotrek/maintenance/filters.py +++ b/geotrek/maintenance/filters.py @@ -130,7 +130,7 @@ class InterventionFilterSet(AltimetryInterventionFilterSet, ZoningFilterSet, Str ON_CHOICES += (('course', _("Outdoor Course")), ('site', _("Outdoor Site")),) bbox = PolygonTopologyFilter(lookup_expr='intersects') - year = MultipleChoiceFilter(choices=Intervention.objects.year_choices(), + year = MultipleChoiceFilter(choices=(('', '---------'),), field_name='date', lookup_expr='year', label=_("Year")) on = ChoiceFilter(field_name='target_type__model', choices=ON_CHOICES, label=_("On"), empty_label=_("On")) area_type = InterventionIntersectionFilterRestrictedAreaType(label=_('Restricted area type'), required=False, @@ -146,12 +146,16 @@ class Meta(StructureRelatedFilterSet.Meta): 'status', 'type', 'stake', 'subcontracting', 'project', 'on', ] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['year'].choices = Intervention.objects.year_choices() + class ProjectFilterSet(StructureRelatedFilterSet): bbox = PythonPolygonFilter(field_name='geom') year = MultipleChoiceFilter( label=_("Year of activity"), method='filter_year', - choices=lambda: Project.objects.year_choices() # Could change over time + choices=(('', '---------'),) ) city = ProjectIntersectionFilterCity(label=_('City'), lookup_expr='intersects', required=False) district = ProjectIntersectionFilterDistrict(label=_('District'), lookup_expr='intersects', required=False) @@ -170,3 +174,7 @@ def filter_year(self, qs, name, values): for value in values: q |= Q(begin_year__lte=value, end_year__gte=value) return qs.filter(q) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.fields['year'].choices = Project.objects.year_choices() diff --git a/geotrek/maintenance/managers.py b/geotrek/maintenance/managers.py index 0cb2a00c15..2132bae747 100644 --- a/geotrek/maintenance/managers.py +++ b/geotrek/maintenance/managers.py @@ -6,8 +6,9 @@ class InterventionManager(NoDeleteManager): def year_choices(self): - return self.existing().filter(date__isnull=False).annotate(year=ExtractYear('date')) \ - .order_by('-year').distinct().values_list('year', 'year') + values = self.existing().filter(date__isnull=False).annotate(year=ExtractYear('date')) \ + .order_by('-year').distinct().values_list('year', flat=True) + return [(year, year) for year in values] class ProjectManager(NoDeleteManager): diff --git a/geotrek/signage/filters.py b/geotrek/signage/filters.py index 431360027f..5ea8090145 100644 --- a/geotrek/signage/filters.py +++ b/geotrek/signage/filters.py @@ -29,7 +29,7 @@ class SignageFilterSet(AltimetryPointFilterSet, ValidTopologyFilterSet, ZoningFi description = CharFilter(label=_('Description'), lookup_expr='icontains') implantation_year = MultipleChoiceFilter(choices=(('', '---------'),)) intervention_year = MultipleChoiceFilter(label=_("Intervention year"), method='filter_intervention_year', - choices=Intervention.objects.year_choices()) + choices=(('', '---------'),)) trail = TopologyFilterTrail(label=_('Trail'), required=False) provider = ChoiceFilter( field_name='provider', @@ -48,6 +48,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.form.fields['implantation_year'].choices = Signage.objects.implantation_year_choices() self.form.fields['provider'].choices = Signage.objects.provider_choices() + self.form.fields['intervention_year'].choices = Intervention.objects.year_choices() def filter_intervention_year(self, qs, name, value): signage_ct = ContentType.objects.get_for_model(Signage) diff --git a/geotrek/tourism/filters.py b/geotrek/tourism/filters.py index 7ab4177d23..8114f93c71 100644 --- a/geotrek/tourism/filters.py +++ b/geotrek/tourism/filters.py @@ -43,20 +43,6 @@ class Meta(StructureRelatedFilterSet.Meta): ] -class AfterFilter(django_filters.DateFilter): - def filter(self, qs, value): - if not value: - return qs - return qs.filter(end_date__gte=value) - - -class BeforeFilter(django_filters.DateFilter): - def filter(self, qs, value): - if not value: - return qs - return qs.filter(begin_date__lte=value) - - class CompletedFilter(django_filters.BooleanFilter): """ Filter events with end_date in past (event completed) @@ -80,8 +66,8 @@ def filter(self, qs, value): class TouristicEventFilterSet(ZoningFilterSet, StructureRelatedFilterSet): - after = AfterFilter(label=_("After")) - before = BeforeFilter(label=_("Before")) + after = django_filters.DateFilter(label=_("After"), lookup_expr='gte', field_name='end_date') + before = django_filters.DateFilter(label=_("Before"), lookup_expr='lte', field_name='begin_date') completed = CompletedFilter(label=_("Completed")) provider = ChoiceFilter( field_name='provider', diff --git a/geotrek/tourism/tests/test_filters.py b/geotrek/tourism/tests/test_filters.py index 4e60b5db51..ab9c6520fa 100644 --- a/geotrek/tourism/tests/test_filters.py +++ b/geotrek/tourism/tests/test_filters.py @@ -2,10 +2,12 @@ from geotrek.tourism.tests.factories import TouristicEventFactory from geotrek.tourism.models import TouristicEvent -from geotrek.tourism.filters import CompletedFilter, BeforeFilter, AfterFilter +from geotrek.tourism.filters import CompletedFilter, TouristicEventFilterSet -class FilterList(TestCase): +class TouristicEventFilterSetTestCase(TestCase): + filter_class = TouristicEventFilterSet + @classmethod def setUpTestData(cls): # TouristicEvent : end_date = datetime.today() @@ -14,17 +16,24 @@ def setUpTestData(cls): def test_touristicevent_filter_completed(self): cf = CompletedFilter() - bf = BeforeFilter() - af = AfterFilter() # date <= Today (True) : completed # date >= Today (False) : not completed self.assertEqual(cf.filter(self.qs, False).count(), 1) self.assertEqual(cf.filter(self.qs, True).count(), 0) + + def test_before_filter(self): + filter = self.filter_class(data={'before': '2000-01-01'}) # Before : date <= date_of_filter - self.assertEqual(bf.filter(self.qs, '2000-01-01').count(), 0) - self.assertEqual(bf.filter(self.qs, '2150-01-01').count(), 1) - self.assertEqual(bf.filter(self.qs, '2300-01-01').count(), 1) - # After : date >= date_of_filter - self.assertEqual(af.filter(self.qs, '2000-01-01').count(), 1) - self.assertEqual(af.filter(self.qs, '2150-01-01').count(), 1) - self.assertEqual(af.filter(self.qs, '2300-01-01').count(), 0) + self.assertEqual(filter.qs.count(), 0) + filter = self.filter_class(data={'before': '2150-01-01'}) + self.assertEqual(filter.qs.count(), 1) + filter = self.filter_class(data={'before': '2300-01-01'}) + self.assertEqual(filter.qs.count(), 1) + + def test_after_filter(self): + filter = self.filter_class(data={'after': '2000-01-01'}) + self.assertEqual(filter.qs.count(), 1) + filter = self.filter_class(data={'after': '2150-01-01'}) + self.assertEqual(filter.qs.count(), 1) + filter = self.filter_class(data={'after': '2300-01-01'}) + self.assertEqual(filter.qs.count(), 0)