From 75ea651d09224cb58383d679db4507231a58ff16 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 15:15:19 +0000 Subject: [PATCH 01/25] update to django5 --- api/pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index ac37b7bf869f..844a2c4501f9 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -104,7 +104,7 @@ enabled = true [tool.poetry.dependencies] python = ">3.11,<3.13" -django = "~4.2.27" +django = ">=5,<6" rudder-sdk-python = "~2.0.2" segment-analytics-python = "~2.2.3" backoff = "~2.2.1" @@ -240,7 +240,7 @@ types-psycopg2 = "^2.9.21.20250121" types-python-dateutil = "^2.9.0.20241206" types-pytz = "^2025.1.0.20250204" ruff = "^0.9.7" -flagsmith-common = { version = "*", extras = ["test-tools"] } +flagsmith-common = { git = "https://github.com/flagsmith/flagsmith-common", branch = "deps/django-5-upgrade", extras = ["test-tools"] } pytest-responses = "^0.5.1" diff-cover = "^10.1.0" From 29c5815458539f18010840a0ba16966164c16fbd Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 15:19:03 +0000 Subject: [PATCH 02/25] Update django-debug-toolbar --- api/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index 844a2c4501f9..5ce13ef94999 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -131,7 +131,7 @@ python-http-client = "~3.3.7" django-health-check = "~3.18.2" django-admin-sso = "~5.2.0" drf-spectacular = "~0.28.0" -django-debug-toolbar = "~3.2.1" +django-debug-toolbar = "^3.2.1" sentry-sdk = "~2.8.0" environs = "^14.1.1" django-lifecycle = "~1.2.4" From 392dd99533fd549a33e01e248eeb8715f7d65bdb Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 15:21:38 +0000 Subject: [PATCH 03/25] Update django-softdelete --- api/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index 5ce13ef94999..1b2d0423cc73 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -144,7 +144,7 @@ asgiref = "~3.8.1" djangorestframework-api-key = "~2.2.0" pymemcache = "~4.0.0" google-re2 = "^1.0" -django-softdelete = "~0.10.5" +django-softdelete = "^0.11.5" simplejson = "~3.19.1" djoser = "~2.3.0" django-storages = "~1.10.1" From 048bc69350ec2a5e69dc6ce38392779be837998f Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 15:23:55 +0000 Subject: [PATCH 04/25] remove uses of index_together --- api/app_analytics/models.py | 2 +- api/environments/identities/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/app_analytics/models.py b/api/app_analytics/models.py index aa55e2e17cbc..98f21eb4e0d2 100644 --- a/api/app_analytics/models.py +++ b/api/app_analytics/models.py @@ -62,7 +62,7 @@ class APIUsageRaw(models.Model): labels = HStoreField(default=dict) class Meta: - index_together = (("environment_id", "created_at"),) + indexes = [models.Index(fields=["environment_id", "created_at"])] class AbstractBucket(LifecycleModelMixin, models.Model): # type: ignore[misc] diff --git a/api/environments/identities/models.py b/api/environments/identities/models.py index 73b374f619ac..441248990516 100644 --- a/api/environments/identities/models.py +++ b/api/environments/identities/models.py @@ -39,7 +39,7 @@ class Meta: # Note that the environment / created_date index is added only to postgres, so we can add it concurrently to # avoid any downtime. If people using MySQL / Oracle have issues with poor performance on the identities table, # we can provide them the SQL to add it manually in a small window of downtime. - index_together = (("environment", "created_date"),) + indexes = [models.Index(fields=["environment", "created_date"])] def natural_key(self): # type: ignore[no-untyped-def] return self.identifier, self.environment.api_key From 8d185aa07428b706b4cd47671a3b2dd20c87f8b4 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 15:25:48 +0000 Subject: [PATCH 05/25] move django-debug-toolbar to dev dependency and loosen dependency specification --- api/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index 1b2d0423cc73..3fa7e99b6233 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -131,7 +131,6 @@ python-http-client = "~3.3.7" django-health-check = "~3.18.2" django-admin-sso = "~5.2.0" drf-spectacular = "~0.28.0" -django-debug-toolbar = "^3.2.1" sentry-sdk = "~2.8.0" environs = "^14.1.1" django-lifecycle = "~1.2.4" @@ -243,6 +242,7 @@ ruff = "^0.9.7" flagsmith-common = { git = "https://github.com/flagsmith/flagsmith-common", branch = "deps/django-5-upgrade", extras = ["test-tools"] } pytest-responses = "^0.5.1" diff-cover = "^10.1.0" +django-debug-toolbar = "*" [build-system] requires = ["poetry>=2.0.0"] From 98dda95a85c24143fa065c2652d0722fa15800a2 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 15:34:54 +0000 Subject: [PATCH 06/25] add rename index migrations --- ...7_rename_environment_id_created_at_index.py | 18 ++++++++++++++++++ ...06_rename_environment_created_date_index.py | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 api/app_analytics/migrations/0007_rename_environment_id_created_at_index.py create mode 100644 api/environments/identities/migrations/0006_rename_environment_created_date_index.py diff --git a/api/app_analytics/migrations/0007_rename_environment_id_created_at_index.py b/api/app_analytics/migrations/0007_rename_environment_id_created_at_index.py new file mode 100644 index 000000000000..f2783cbae638 --- /dev/null +++ b/api/app_analytics/migrations/0007_rename_environment_id_created_at_index.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.9 on 2025-12-31 15:34 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("app_analytics", "0006_add_labels"), + ] + + operations = [ + migrations.RenameIndex( + model_name="apiusageraw", + new_name="app_analyti_environ_b61cad_idx", + old_fields=("environment_id", "created_at"), + ), + ] diff --git a/api/environments/identities/migrations/0006_rename_environment_created_date_index.py b/api/environments/identities/migrations/0006_rename_environment_created_date_index.py new file mode 100644 index 000000000000..2bfdc00e9e0b --- /dev/null +++ b/api/environments/identities/migrations/0006_rename_environment_created_date_index.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.9 on 2025-12-31 15:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("identities", "0005_revert_sanitized_identifiers"), + ] + + operations = [ + migrations.RenameIndex( + model_name="identity", + new_name="environment_environ_341dc9_idx", + old_fields=("environment", "created_date"), + ), + ] From 4223e543ec84de4caa502ae2fc2555730725003b Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 15:41:39 +0000 Subject: [PATCH 07/25] Add no-op migration needed for through model --- api/poetry.lock | 25 ++++++++--------- ...gh_fields_metadata_for_django_5_upgrade.py | 28 +++++++++++++++++++ 2 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 api/users/migrations/0045_add_through_fields_metadata_for_django_5_upgrade.py diff --git a/api/poetry.lock b/api/poetry.lock index f3c8405f9e48..19ea09ae92dc 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1592,38 +1592,37 @@ sftp = ["paramiko"] [[package]] name = "django-stubs" -version = "5.1.3" +version = "5.2.8" description = "Mypy stubs for Django" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main", "dev"] files = [ - {file = "django_stubs-5.1.3-py3-none-any.whl", hash = "sha256:716758ced158b439213062e52de6df3cff7c586f9f9ad7ab59210efbea5dfe78"}, - {file = "django_stubs-5.1.3.tar.gz", hash = "sha256:8c230bc5bebee6da282ba8a27ad1503c84a0c4cd2f46e63d149e76d2a63e639a"}, + {file = "django_stubs-5.2.8-py3-none-any.whl", hash = "sha256:a3c63119fd7062ac63d58869698d07c9e5ec0561295c4e700317c54e8d26716c"}, + {file = "django_stubs-5.2.8.tar.gz", hash = "sha256:9bba597c9a8ed8c025cae4696803d5c8be1cf55bfc7648a084cbf864187e2f8b"}, ] [package.dependencies] -asgiref = "*" django = "*" -django-stubs-ext = ">=5.1.3" -types-PyYAML = "*" +django-stubs-ext = ">=5.2.8" +types-pyyaml = "*" typing-extensions = ">=4.11.0" [package.extras] -compatible-mypy = ["mypy (>=1.12,<1.16)"] +compatible-mypy = ["mypy (>=1.13,<1.20)"] oracle = ["oracledb"] -redis = ["redis"] +redis = ["redis", "types-redis"] [[package]] name = "django-stubs-ext" -version = "5.1.3" +version = "5.2.8" description = "Monkey-patching and extensions for django-stubs" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main", "dev"] files = [ - {file = "django_stubs_ext-5.1.3-py3-none-any.whl", hash = "sha256:64561fbc53e963cc1eed2c8eb27e18b8e48dcb90771205180fe29fc8a59e55fd"}, - {file = "django_stubs_ext-5.1.3.tar.gz", hash = "sha256:3e60f82337f0d40a362f349bf15539144b96e4ceb4dbd0239be1cd71f6a74ad0"}, + {file = "django_stubs_ext-5.2.8-py3-none-any.whl", hash = "sha256:1dd5470c9675591362c78a157a3cf8aec45d0e7a7f0cf32f227a1363e54e0652"}, + {file = "django_stubs_ext-5.2.8.tar.gz", hash = "sha256:b39938c46d7a547cd84e4a6378dbe51a3dd64d70300459087229e5fee27e5c6b"}, ] [package.dependencies] diff --git a/api/users/migrations/0045_add_through_fields_metadata_for_django_5_upgrade.py b/api/users/migrations/0045_add_through_fields_metadata_for_django_5_upgrade.py new file mode 100644 index 000000000000..0121c29750a0 --- /dev/null +++ b/api/users/migrations/0045_add_through_fields_metadata_for_django_5_upgrade.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.9 on 2025-12-31 15:38 +# Note: This migration is a no-op, and just adds the `through_fields` attribute +# seemingly needed by the state in django5 + + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("users", "0044_remove_users_from_groups_in_orgs_they_do_not_belong_to"), + ] + + operations = [ + migrations.AlterField( + model_name="userpermissiongroup", + name="users", + field=models.ManyToManyField( + blank=True, + related_name="permission_groups", + through="users.UserPermissionGroupMembership", + through_fields=["userpermissiongroup", "ffadminuser"], # type: ignore[arg-type] + to=settings.AUTH_USER_MODEL, + ), + ), + ] From 384497d2b6ed948f1cdcacb9ccfda749294e0c58 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 16:57:55 +0000 Subject: [PATCH 08/25] Typing fixes / ignore additions following django 5 upgrade --- api/audit/views.py | 2 +- .../mfa/trench/command/remove_backup_code.py | 2 ++ api/custom_auth/mfa/trench/views/base.py | 7 ++++--- api/environments/identities/managers.py | 2 +- api/environments/managers.py | 2 +- api/features/admin.py | 4 ++-- api/features/feature_states/permissions.py | 2 +- api/features/import_export/permissions.py | 8 ++++---- api/features/permissions.py | 10 +++++----- api/features/versioning/permissions.py | 20 +++++++++---------- api/features/versioning/versioning_service.py | 6 +++--- api/organisations/permissions/permissions.py | 2 +- api/projects/code_references/permissions.py | 2 +- .../test_unit_sales_dashboard_views.py | 12 +++++------ api/users/models.py | 9 +++++++++ 15 files changed, 51 insertions(+), 39 deletions(-) diff --git a/api/audit/views.py b/api/audit/views.py index 994868dbbabd..1e65870382df 100644 --- a/api/audit/views.py +++ b/api/audit/views.py @@ -108,7 +108,7 @@ def _get_organisation(self) -> Organisation | None: Since we're applying the base filters to the query set """ - return ( + return ( # type: ignore[no-any-return] self.request.user.organisations.filter( # type: ignore[union-attr] userorganisation__role=OrganisationRole.ADMIN ) diff --git a/api/custom_auth/mfa/trench/command/remove_backup_code.py b/api/custom_auth/mfa/trench/command/remove_backup_code.py index 120a11ed1bbd..3a3731012137 100644 --- a/api/custom_auth/mfa/trench/command/remove_backup_code.py +++ b/api/custom_auth/mfa/trench/command/remove_backup_code.py @@ -11,6 +11,8 @@ def remove_backup_code_command(user_id: Any, method_name: str, code: str) -> Non .values_list("_backup_codes", flat=True) .first() ) + if serialized_codes is None: + return codes = MFAMethod._BACKUP_CODES_DELIMITER.join( _remove_code_from_set( # type: ignore[arg-type] backup_codes=set(serialized_codes.split(MFAMethod._BACKUP_CODES_DELIMITER)), diff --git a/api/custom_auth/mfa/trench/views/base.py b/api/custom_auth/mfa/trench/views/base.py index 66f3f783667c..8078e74b258d 100644 --- a/api/custom_auth/mfa/trench/views/base.py +++ b/api/custom_auth/mfa/trench/views/base.py @@ -38,7 +38,7 @@ def post(request: Request, method: str) -> Response: try: user = request.user mfa = create_mfa_method_command( - user_id=user.id, + user_id=user.id, # type: ignore[arg-type] name=method, ) except MFAValidationError as cause: @@ -57,7 +57,7 @@ def post(request: Request, method: str) -> Response: if not serializer.is_valid(): return Response(status=HTTP_400_BAD_REQUEST, data=serializer.errors) backup_codes = activate_mfa_method_command( - user_id=request.user.id, + user_id=request.user.id, # type: ignore[arg-type] name=method, code=serializer.validated_data["code"], ) @@ -71,7 +71,8 @@ class MFAMethodDeactivationView(APIView): def post(request: Request, method: str) -> Response: try: deactivate_mfa_method_command( - mfa_method_name=method, user_id=request.user.id + mfa_method_name=method, + user_id=request.user.id, # type: ignore[arg-type] ) return Response(status=HTTP_204_NO_CONTENT) except MFAValidationError as cause: diff --git a/api/environments/identities/managers.py b/api/environments/identities/managers.py index a0e5fb2f7db2..4a34bccbb592 100644 --- a/api/environments/identities/managers.py +++ b/api/environments/identities/managers.py @@ -34,7 +34,7 @@ def with_context( self, integrations: "Iterable[IntegrationConfig] | None" = None, extra_select_related: "Iterable[str] | None" = None, - extra_prefetch_related: "Iterable[str | Prefetch] | None" = None, + extra_prefetch_related: "Iterable[str | Prefetch] | None" = None, # type: ignore[type-arg] ) -> "QuerySet[Identity]": from integrations.integration import IDENTITY_INTEGRATIONS diff --git a/api/environments/managers.py b/api/environments/managers.py index d191316ff236..db7790736929 100644 --- a/api/environments/managers.py +++ b/api/environments/managers.py @@ -11,7 +11,7 @@ def filter_for_document_builder( # type: ignore[no-untyped-def] self, *args, extra_select_related: list[str] | None = None, - extra_prefetch_related: list[Prefetch | str] | None = None, + extra_prefetch_related: list[Prefetch | str] | None = None, # type: ignore[type-arg] **kwargs, ): return ( diff --git a/api/features/admin.py b/api/features/admin.py index 9d0a8099232c..7d220507eadb 100644 --- a/api/features/admin.py +++ b/api/features/admin.py @@ -44,11 +44,11 @@ class FeatureSegmentAdmin(admin.ModelAdmin): # type: ignore[type-arg] model = FeatureSegment def add_view(self, *args, **kwargs): # type: ignore[no-untyped-def] - self.exclude = ("priority",) + self.exclude = ("priority",) # type: ignore[misc] return super(FeatureSegmentAdmin, self).add_view(*args, **kwargs) def change_view(self, *args, **kwargs): # type: ignore[no-untyped-def] - self.exclude = () + self.exclude = () # type: ignore[misc] return super(FeatureSegmentAdmin, self).change_view(*args, **kwargs) diff --git a/api/features/feature_states/permissions.py b/api/features/feature_states/permissions.py index d7bf46e80d9f..fc434c587e05 100644 --- a/api/features/feature_states/permissions.py +++ b/api/features/feature_states/permissions.py @@ -14,6 +14,6 @@ def has_permission(self, request: Request, view: APIView) -> bool: except Environment.DoesNotExist: return False - return request.user.has_environment_permission( # type: ignore[union-attr] + return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] UPDATE_FEATURE_STATE, environment ) diff --git a/api/features/import_export/permissions.py b/api/features/import_export/permissions.py index cd004156366c..237697cb8320 100644 --- a/api/features/import_export/permissions.py +++ b/api/features/import_export/permissions.py @@ -29,7 +29,7 @@ def has_permission(self, request: Request, view: APIView) -> bool: return False environment = Environment.objects.get(id=request.data["environment_id"]) - return request.user.is_environment_admin(environment) # type: ignore[union-attr] + return request.user.is_environment_admin(environment) # type: ignore[union-attr,no-any-return] class DownloadFeatureExportPermissions(IsAuthenticated): @@ -39,7 +39,7 @@ def has_permission(self, request: Request, view: APIView) -> bool: feature_export = FeatureExport.objects.get(id=view.kwargs["feature_export_id"]) - return request.user.is_environment_admin(feature_export.environment) # type: ignore[union-attr] + return request.user.is_environment_admin(feature_export.environment) # type: ignore[union-attr,no-any-return] class FeatureExportListPermissions(IsAuthenticated): @@ -50,7 +50,7 @@ def has_permission(self, request: Request, view: ListAPIView) -> bool: # type: project = Project.objects.get(id=view.kwargs["project_pk"]) # The user will only see environment feature exports # that the user is an environment admin. - return request.user.has_project_permission(VIEW_PROJECT, project) # type: ignore[union-attr] + return request.user.has_project_permission(VIEW_PROJECT, project) # type: ignore[union-attr,no-any-return] class FeatureImportListPermissions(IsAuthenticated): @@ -61,4 +61,4 @@ def has_permission(self, request: Request, view: ListAPIView) -> bool: # type: project = Project.objects.get(id=view.kwargs["project_pk"]) # The user will only see environment feature imports # that the user is an environment admin. - return request.user.has_project_permission(VIEW_PROJECT, project) # type: ignore[union-attr] + return request.user.has_project_permission(VIEW_PROJECT, project) # type: ignore[union-attr,no-any-return] diff --git a/api/features/permissions.py b/api/features/permissions.py index 72fbb439028c..bf19cca8cc1f 100644 --- a/api/features/permissions.py +++ b/api/features/permissions.py @@ -120,10 +120,10 @@ def has_permission(self, request: Request, view: GenericViewSet) -> bool: # typ tag_ids = list(feature.tags.values_list("id", flat=True)) - return request.user.has_environment_permission( # type: ignore[union-attr] + return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] required_permission, environment, - tag_ids=tag_ids, # type: ignore[arg-type] + tag_ids=tag_ids, ) return False @@ -144,10 +144,10 @@ def has_object_permission( if permission in TAG_SUPPORTED_ENVIRONMENT_PERMISSIONS: tag_ids = list(obj.feature.tags.values_list("id", flat=True)) - return request.user.has_environment_permission( # type: ignore[union-attr] + return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] permission, - environment=obj.environment, # type: ignore[arg-type] - tag_ids=tag_ids, # type: ignore[arg-type] + environment=obj.environment, + tag_ids=tag_ids, ) diff --git a/api/features/versioning/permissions.py b/api/features/versioning/permissions.py index f0d362593e45..7795f32d95da 100644 --- a/api/features/versioning/permissions.py +++ b/api/features/versioning/permissions.py @@ -31,10 +31,10 @@ def has_permission(self, request: Request, view: GenericViewSet) -> bool: # typ feature = Feature.objects.get(id=feature_id, project=environment.project) tag_ids = list(feature.tags.values_list("id", flat=True)) - return request.user.has_environment_permission( # type: ignore[union-attr] + return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] permission=required_permission, environment=environment, - tag_ids=tag_ids, # type: ignore[arg-type] + tag_ids=tag_ids, ) def has_object_permission( @@ -53,10 +53,10 @@ def has_object_permission( if required_permission in TAG_SUPPORTED_ENVIRONMENT_PERMISSIONS: tag_ids = list(obj.feature.tags.values_list("id", flat=True)) - return request.user.has_environment_permission( # type: ignore[union-attr] + return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] permission=required_permission, environment=obj.environment, - tag_ids=tag_ids, # type: ignore[arg-type] + tag_ids=tag_ids, ) @@ -73,11 +73,11 @@ def has_permission(self, request: Request, view: GenericViewSet) -> bool: # typ environment = Environment.objects.get(id=environment_pk) if view.action == "list": - return request.user.has_environment_permission( # type: ignore[union-attr] + return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] permission=VIEW_ENVIRONMENT, environment=environment ) - return request.user.has_environment_permission( # type: ignore[union-attr] + return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] permission=UPDATE_FEATURE_STATE, environment=environment ) @@ -88,12 +88,12 @@ def has_object_permission( obj: FeatureState, ) -> bool: if view.action == "retrieve": - return request.user.has_environment_permission( # type: ignore[union-attr] + return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] permission=VIEW_ENVIRONMENT, - environment=obj.environment, # type: ignore[arg-type] + environment=obj.environment, ) - return request.user.has_environment_permission( # type: ignore[union-attr] + return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] permission=UPDATE_FEATURE_STATE, - environment=obj.environment, # type: ignore[arg-type] + environment=obj.environment, ) diff --git a/api/features/versioning/versioning_service.py b/api/features/versioning/versioning_service.py index b8d5d40d47ff..e027692d384c 100644 --- a/api/features/versioning/versioning_service.py +++ b/api/features/versioning/versioning_service.py @@ -33,7 +33,7 @@ def get_environment_flags_list( additional_filters: Q = None, # type: ignore[assignment] additional_select_related_args: typing.Iterable[str] = None, # type: ignore[assignment] additional_prefetch_related_args: typing.Iterable[ - typing.Union[str, Prefetch] + typing.Union[str, Prefetch[typing.Any]] ] = None, # type: ignore[assignment] from_replica: bool = False, ) -> list[FeatureState]: @@ -64,7 +64,7 @@ def get_environment_flags_dict( additional_filters: Q = None, # type: ignore[assignment] additional_select_related_args: typing.Iterable[str] = None, # type: ignore[assignment] additional_prefetch_related_args: typing.Iterable[ - typing.Union[str, Prefetch] + typing.Union[str, Prefetch[typing.Any]] ] = None, # type: ignore[assignment] key_function: typing.Callable[[FeatureState], tuple] = None, # type: ignore[type-arg,assignment] from_replica: bool = False, @@ -507,7 +507,7 @@ def _get_feature_states_queryset( additional_filters: Q = None, # type: ignore[assignment] additional_select_related_args: typing.Iterable[str] = None, # type: ignore[assignment] additional_prefetch_related_args: typing.Iterable[ - typing.Union[str, Prefetch] + typing.Union[str, Prefetch[typing.Any]] ] = None, # type: ignore[assignment] from_replica: bool = False, ) -> QuerySet[FeatureState]: diff --git a/api/organisations/permissions/permissions.py b/api/organisations/permissions/permissions.py index 5d9fa9c16e39..d31508c1a523 100644 --- a/api/organisations/permissions/permissions.py +++ b/api/organisations/permissions/permissions.py @@ -230,4 +230,4 @@ def has_permission(self, request: Request, view: View) -> bool: return False # All organisation users can see api usage notifications. - return request.user.belongs_to(view.kwargs.get("organisation_pk")) # type: ignore[union-attr] + return request.user.belongs_to(view.kwargs.get("organisation_pk")) # type: ignore[union-attr,no-any-return] diff --git a/api/projects/code_references/permissions.py b/api/projects/code_references/permissions.py index d590a1720cc5..94744f10e2bd 100644 --- a/api/projects/code_references/permissions.py +++ b/api/projects/code_references/permissions.py @@ -14,7 +14,7 @@ def has_permission(self, request: Request, view: APIView) -> bool: assert not isinstance(request.user, AnonymousUser) project = Project.objects.get(id=view.kwargs["project_pk"]) - return request.user.has_project_permission(VIEW_PROJECT, project) + return request.user.has_project_permission(VIEW_PROJECT, project) # type: ignore[no-any-return] class SubmitFeatureFlagCodeReferences(_BaseCodeReferencePermission): diff --git a/api/tests/unit/sales_dashboard/test_unit_sales_dashboard_views.py b/api/tests/unit/sales_dashboard/test_unit_sales_dashboard_views.py index 102b6d4eeb93..21bd46c1fa1d 100644 --- a/api/tests/unit/sales_dashboard/test_unit_sales_dashboard_views.py +++ b/api/tests/unit/sales_dashboard/test_unit_sales_dashboard_views.py @@ -96,7 +96,7 @@ def test_list_organisations_search_by_name( # Then assert response.status_code == 200 - assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[attr-defined] + assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[index] def test_list_organisations_search_by_subscription_id( @@ -114,7 +114,7 @@ def test_list_organisations_search_by_subscription_id( # Then assert response.status_code == 200 - assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[attr-defined] + assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[index] def test_list_organisations_search_by_user_email( @@ -132,7 +132,7 @@ def test_list_organisations_search_by_user_email( # Then assert response.status_code == 200 - assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[attr-defined] + assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[index] def test_list_organisations_search_by_user_email_for_non_existent_user( @@ -152,7 +152,7 @@ def test_list_organisations_search_by_user_email_for_non_existent_user( # Then assert response.status_code == 200 - assert list(response.context_data["organisation_list"]) == [] # type: ignore[attr-defined] + assert list(response.context_data["organisation_list"]) == [] # type: ignore[index] def test_list_organisations_search_by_domain( @@ -171,7 +171,7 @@ def test_list_organisations_search_by_domain( # Then assert response.status_code == 200 - assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[attr-defined] + assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[index] def test_list_organisations_filter_plan( @@ -190,7 +190,7 @@ def test_list_organisations_filter_plan( # Then assert response.status_code == 200 - assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[attr-defined] + assert list(response.context_data["organisation_list"]) == [organisation] # type: ignore[index] def test_list_organisations_fails_if_not_staff( diff --git a/api/users/models.py b/api/users/models.py index ddc59b2f5c40..e3189af3239e 100644 --- a/api/users/models.py +++ b/api/users/models.py @@ -1,4 +1,6 @@ import logging +import secrets +import string import typing import uuid @@ -105,6 +107,13 @@ def get_by_natural_key(self, email): # type: ignore[no-untyped-def] # Used to allow case insensitive login return self.get(email__iexact=email) + def make_random_password( + self, + length: int = 10, + allowed_chars: str = string.ascii_letters + string.digits, + ) -> str: + return "".join(secrets.choice(allowed_chars) for _ in range(length)) + class FFAdminUser(LifecycleModel, AbstractUser): # type: ignore[django-manager-missing,misc] organisations = models.ManyToManyField( From 65b0e0dad45931624224cd2430031d26278e2fed Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 17:11:40 +0000 Subject: [PATCH 09/25] Replace uses of timezone.utc --- .../organisations/test_unit_organisations_views.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/api/tests/unit/organisations/test_unit_organisations_views.py b/api/tests/unit/organisations/test_unit_organisations_views.py index 0aabd6ce7012..0c502ecf9f2a 100644 --- a/api/tests/unit/organisations/test_unit_organisations_views.py +++ b/api/tests/unit/organisations/test_unit_organisations_views.py @@ -1,5 +1,6 @@ import json from datetime import datetime, timedelta +from datetime import timezone as dttz from typing import Type from unittest import mock from unittest.mock import MagicMock @@ -785,7 +786,7 @@ def test_chargebee_webhook( 15, 33, 9, - tzinfo=timezone.utc, # type: ignore[attr-defined] + tzinfo=dttz.utc, ) assert subscription_cache.current_billing_term_starts_at == datetime( 2023, @@ -794,7 +795,7 @@ def test_chargebee_webhook( 15, 33, 9, - tzinfo=timezone.utc, # type: ignore[attr-defined] + tzinfo=dttz.utc, # type: ignore[attr-defined] ) assert subscription_cache.allowed_projects is None assert subscription_cache.allowed_30d_api_calls == api_calls @@ -836,9 +837,7 @@ def test_when_subscription_is_set_to_non_renewing_then_cancellation_date_set_and subscription.refresh_from_db() assert subscription.cancellation_date == datetime.utcfromtimestamp( current_term_end - ).replace( - tzinfo=timezone.utc # type: ignore[attr-defined] - ) + ).replace(tzinfo=dttz.utc) # and assert len(mail.outbox) == 1 @@ -915,9 +914,7 @@ def test_when_subscription_is_cancelled_then_cancellation_date_set_and_alert_sen subscription.refresh_from_db() assert subscription.cancellation_date == datetime.utcfromtimestamp( current_term_end - ).replace( - tzinfo=timezone.utc # type: ignore[attr-defined] - ) + ).replace(tzinfo=dttz.utc) # and assert len(mail.outbox) == 1 From bf2ccbb5b3779c6709bd190566fa750ee8ec9713 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 17:21:21 +0000 Subject: [PATCH 10/25] Update djoser to 2.3.3 --- api/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index 3fa7e99b6233..64e9a8472e0b 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -145,7 +145,7 @@ pymemcache = "~4.0.0" google-re2 = "^1.0" django-softdelete = "^0.11.5" simplejson = "~3.19.1" -djoser = "~2.3.0" +djoser = "^2.3.0" django-storages = "~1.10.1" django-environ = "~0.4.5" influxdb-client = "~1.28.0" From 79cf1ab06c8155017299eda845f7a374a46a0082 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 17:31:16 +0000 Subject: [PATCH 11/25] Use `.value` for enum following django upgrade --- api/features/feature_health/services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/features/feature_health/services.py b/api/features/feature_health/services.py index cc4b48c574f1..11958603c40f 100644 --- a/api/features/feature_health/services.py +++ b/api/features/feature_health/services.py @@ -145,7 +145,7 @@ def dismiss_feature_health_event( FeatureHealthEvent.objects.create( feature=feature_health_event.feature, environment=feature_health_event.environment, - type=FeatureHealthEventType.HEALTHY, + type=FeatureHealthEventType.HEALTHY.value, reason=json.dumps(reason), provider_name=feature_health_event.provider_name, external_id=feature_health_event.external_id, From 0622b3fdecfe190c7d151e3ce96d72a003dc2be0 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 17:36:55 +0000 Subject: [PATCH 12/25] Remove DEBUG from tests --- api/app/settings/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/app/settings/test.py b/api/app/settings/test.py index 23c9f96ea7de..0bdfc131c1ea 100644 --- a/api/app/settings/test.py +++ b/api/app/settings/test.py @@ -67,4 +67,4 @@ ENABLE_POSTPONE_DECORATOR = False -DEBUG = True +DEBUG = False From 8a2e9360b566eb9d0031bf97064a5d9c14062135 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 17:46:29 +0000 Subject: [PATCH 13/25] Replace other uses of timezone.utc --- api/organisations/chargebee/webhook_handlers.py | 11 ++++------- .../organisations/test_unit_organisations_views.py | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/api/organisations/chargebee/webhook_handlers.py b/api/organisations/chargebee/webhook_handlers.py index 242b0db3ead6..ab32b7f4b2d8 100644 --- a/api/organisations/chargebee/webhook_handlers.py +++ b/api/organisations/chargebee/webhook_handlers.py @@ -1,5 +1,6 @@ import logging from datetime import datetime +from datetime import timezone as dttz from django.utils import timezone from rest_framework import status @@ -129,7 +130,7 @@ def process_subscription(request: Request) -> Response: # noqa: C901 cancellation_date = subscription.get("current_term_end") if cancellation_date is not None: cancellation_date = datetime.fromtimestamp(cancellation_date).replace( - tzinfo=timezone.utc # type: ignore[attr-defined] + tzinfo=dttz.utc ) else: cancellation_date = timezone.now() @@ -168,9 +169,7 @@ def process_subscription(request: Request) -> Response: # noqa: C901 else: osic_defaults["current_billing_term_ends_at"] = datetime.fromtimestamp( current_term_end - ).replace( - tzinfo=timezone.utc # type: ignore[attr-defined] - ) + ).replace(tzinfo=dttz.utc) if "current_term_start" in subscription: current_term_start = subscription["current_term_start"] @@ -179,9 +178,7 @@ def process_subscription(request: Request) -> Response: # noqa: C901 else: osic_defaults["current_billing_term_starts_at"] = datetime.fromtimestamp( current_term_start - ).replace( - tzinfo=timezone.utc # type: ignore[attr-defined] - ) + ).replace(tzinfo=dttz.utc) OrganisationSubscriptionInformationCache.objects.update_or_create( organisation_id=existing_subscription.organisation_id, diff --git a/api/tests/unit/organisations/test_unit_organisations_views.py b/api/tests/unit/organisations/test_unit_organisations_views.py index 0c502ecf9f2a..e7593a6cc0af 100644 --- a/api/tests/unit/organisations/test_unit_organisations_views.py +++ b/api/tests/unit/organisations/test_unit_organisations_views.py @@ -795,7 +795,7 @@ def test_chargebee_webhook( 15, 33, 9, - tzinfo=dttz.utc, # type: ignore[attr-defined] + tzinfo=dttz.utc, ) assert subscription_cache.allowed_projects is None assert subscription_cache.allowed_30d_api_calls == api_calls From 93e20f34d926cc56b554d3a413006df8f78d428d Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 17:46:42 +0000 Subject: [PATCH 14/25] Replace assertQuerysetEqual with assertQuerySetEqual --- api/tests/unit/environments/dynamodb/test_unit_migrator.py | 2 +- api/tests/unit/environments/test_unit_environments_models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/tests/unit/environments/dynamodb/test_unit_migrator.py b/api/tests/unit/environments/dynamodb/test_unit_migrator.py index 7b46b4f990b6..a1815879b424 100644 --- a/api/tests/unit/environments/dynamodb/test_unit_migrator.py +++ b/api/tests/unit/environments/dynamodb/test_unit_migrator.py @@ -1,4 +1,4 @@ -from pytest_django.asserts import assertQuerysetEqual as assert_queryset_equal +from pytest_django.asserts import assertQuerySetEqual as assert_queryset_equal from environments.dynamodb.migrator import IdentityMigrator from environments.dynamodb.types import ( diff --git a/api/tests/unit/environments/test_unit_environments_models.py b/api/tests/unit/environments/test_unit_environments_models.py index 6cdca25b44db..2f17845bfc7e 100644 --- a/api/tests/unit/environments/test_unit_environments_models.py +++ b/api/tests/unit/environments/test_unit_environments_models.py @@ -12,7 +12,7 @@ from django.utils import timezone from mypy_boto3_dynamodb.service_resource import Table from pytest_django import DjangoAssertNumQueries -from pytest_django.asserts import assertQuerysetEqual as assert_queryset_equal +from pytest_django.asserts import assertQuerySetEqual as assert_queryset_equal from pytest_mock import MockerFixture from audit.models import AuditLog From 18a3e5de1827c467dac31d067a06b06668a2614d Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 31 Dec 2025 17:58:35 +0000 Subject: [PATCH 15/25] Add pragma no cover --- api/app/urls.py | 2 +- api/custom_auth/mfa/trench/command/remove_backup_code.py | 2 +- api/features/admin.py | 4 ++-- api/features/versioning/permissions.py | 2 +- api/projects/models.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/app/urls.py b/api/app/urls.py index 69fd350419eb..d5b68f85f6ca 100644 --- a/api/app/urls.py +++ b/api/app/urls.py @@ -49,7 +49,7 @@ ), ] -if settings.DEBUG: +if settings.DEBUG: # pragma: no cover urlpatterns = [ re_path(r"^__debug__/", include("debug_toolbar.urls")), ] + urlpatterns diff --git a/api/custom_auth/mfa/trench/command/remove_backup_code.py b/api/custom_auth/mfa/trench/command/remove_backup_code.py index 3a3731012137..873f97b7110a 100644 --- a/api/custom_auth/mfa/trench/command/remove_backup_code.py +++ b/api/custom_auth/mfa/trench/command/remove_backup_code.py @@ -11,7 +11,7 @@ def remove_backup_code_command(user_id: Any, method_name: str, code: str) -> Non .values_list("_backup_codes", flat=True) .first() ) - if serialized_codes is None: + if serialized_codes is None: # pragma: no cover return codes = MFAMethod._BACKUP_CODES_DELIMITER.join( _remove_code_from_set( # type: ignore[arg-type] diff --git a/api/features/admin.py b/api/features/admin.py index 7d220507eadb..1fc7261b559c 100644 --- a/api/features/admin.py +++ b/api/features/admin.py @@ -43,11 +43,11 @@ class FeatureAdmin(SimpleHistoryAdmin): # type: ignore[misc] class FeatureSegmentAdmin(admin.ModelAdmin): # type: ignore[type-arg] model = FeatureSegment - def add_view(self, *args, **kwargs): # type: ignore[no-untyped-def] + def add_view(self, *args, **kwargs): # type: ignore[no-untyped-def] # pragma: no cover self.exclude = ("priority",) # type: ignore[misc] return super(FeatureSegmentAdmin, self).add_view(*args, **kwargs) - def change_view(self, *args, **kwargs): # type: ignore[no-untyped-def] + def change_view(self, *args, **kwargs): # type: ignore[no-untyped-def] # pragma: no cover self.exclude = () # type: ignore[misc] return super(FeatureSegmentAdmin, self).change_view(*args, **kwargs) diff --git a/api/features/versioning/permissions.py b/api/features/versioning/permissions.py index 7795f32d95da..e7072cf1e617 100644 --- a/api/features/versioning/permissions.py +++ b/api/features/versioning/permissions.py @@ -87,7 +87,7 @@ def has_object_permission( view: GenericViewSet, # type: ignore[override,type-arg] obj: FeatureState, ) -> bool: - if view.action == "retrieve": + if view.action == "retrieve": # pragma: no cover return request.user.has_environment_permission( # type: ignore[union-attr,no-any-return] permission=VIEW_ENVIRONMENT, environment=obj.environment, diff --git a/api/projects/models.py b/api/projects/models.py index bf21604a4f95..69443c19f1fa 100644 --- a/api/projects/models.py +++ b/api/projects/models.py @@ -113,7 +113,7 @@ class Project(LifecycleModelMixin, SoftDeleteExportableModel): # type: ignore[d class Meta: ordering = ["id"] - def __str__(self): # type: ignore[no-untyped-def] + def __str__(self): # type: ignore[no-untyped-def] # pragma: no cover return "Project %s" % self.name @property From 105466911e07038e1546949cedf02fb8b1a05098 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Fri, 2 Jan 2026 10:00:44 +0000 Subject: [PATCH 16/25] Remove unnecessary DEBUG value from test settings. --- api/app/settings/test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/api/app/settings/test.py b/api/app/settings/test.py index 0bdfc131c1ea..1f0ab33f395c 100644 --- a/api/app/settings/test.py +++ b/api/app/settings/test.py @@ -66,5 +66,3 @@ """ ENABLE_POSTPONE_DECORATOR = False - -DEBUG = False From 07b1f6495bcb935098bf632bb9ee1c38538d4cf8 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Fri, 2 Jan 2026 16:26:29 +0000 Subject: [PATCH 17/25] Only rename index for postgres as per previous migration --- ...6_rename_environment_created_date_index.py | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/api/environments/identities/migrations/0006_rename_environment_created_date_index.py b/api/environments/identities/migrations/0006_rename_environment_created_date_index.py index 2bfdc00e9e0b..dafee6a41087 100644 --- a/api/environments/identities/migrations/0006_rename_environment_created_date_index.py +++ b/api/environments/identities/migrations/0006_rename_environment_created_date_index.py @@ -1,5 +1,5 @@ # Generated by Django 5.2.9 on 2025-12-31 15:29 - +from common.migrations.helpers import PostgresOnlyRunSQL from django.db import migrations @@ -10,9 +10,19 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RenameIndex( - model_name="identity", - new_name="environment_environ_341dc9_idx", - old_fields=("environment", "created_date"), - ), + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.RenameIndex( + model_name="identity", + new_name="environment_environ_341dc9_idx", + old_fields=("environment", "created_date"), + ), + ], + database_operations=[ + PostgresOnlyRunSQL( + 'ALTER INDEX "environments_identity_environment_id_created_date_idx" RENAME TO "environment_environ_341dc9_idx"', + reverse_sql='ALTER INDEX "environment_environ_341dc9_idx" RENAME TO "environments_identity_environment_id_created_date_idx"' + ) + ], + ) ] From f6c1c4cdfe0420c77997690afe64dedd6f6ace88 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Tue, 6 Jan 2026 10:00:21 +0000 Subject: [PATCH 18/25] Update flagsmith-common --- api/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index 64e9a8472e0b..3cd94729cc5b 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -239,7 +239,7 @@ types-psycopg2 = "^2.9.21.20250121" types-python-dateutil = "^2.9.0.20241206" types-pytz = "^2025.1.0.20250204" ruff = "^0.9.7" -flagsmith-common = { git = "https://github.com/flagsmith/flagsmith-common", branch = "deps/django-5-upgrade", extras = ["test-tools"] } +flagsmith-common = "^3.1.0" pytest-responses = "^0.5.1" diff-cover = "^10.1.0" django-debug-toolbar = "*" From e68f085d482e85a5e547903eba70e91a72845f7a Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 21 Jan 2026 15:49:52 +0100 Subject: [PATCH 19/25] Poetry lock after rebase --- api/poetry.lock | 63 ++++++++----------------------------------------- 1 file changed, 10 insertions(+), 53 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index 19ea09ae92dc..166c4ce5734d 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1268,18 +1268,18 @@ Django = ">=4.2" [[package]] name = "django" -version = "4.2.27" +version = "5.2.10" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main", "auth-controller", "dev", "ldap", "workflows"] files = [ - {file = "django-4.2.27-py3-none-any.whl", hash = "sha256:f393a394053713e7d213984555c5b7d3caeee78b2ccb729888a0774dff6c11a8"}, - {file = "django-4.2.27.tar.gz", hash = "sha256:b865fbe0f4a3d1ee36594c5efa42b20db3c8bbb10dff0736face1c6e4bda5b92"}, + {file = "django-5.2.10-py3-none-any.whl", hash = "sha256:cf85067a64250c95d5f9067b056c5eaa80591929f7e16fbcd997746e40d6c45c"}, + {file = "django-5.2.10.tar.gz", hash = "sha256:74df100784c288c50a2b5cad59631d71214f40f72051d5af3fdf220c20bdbbbe"}, ] [package.dependencies] -asgiref = ">=3.6.0,<4" +asgiref = ">=3.8.1" sqlparse = ">=0.3.1" tzdata = {version = "*", markers = "sys_platform == \"win32\""} @@ -1353,7 +1353,7 @@ version = "3.2.4" description = "A configurable set of panels that display various debug information about the current request/response." optional = false python-versions = ">=3.6" -groups = ["main"] +groups = ["dev"] files = [ {file = "django-debug-toolbar-3.2.4.tar.gz", hash = "sha256:644bbd5c428d3283aa9115722471769cac1bec189edf3a0c855fd8ff870375a9"}, {file = "django_debug_toolbar-3.2.4-py3-none-any.whl", hash = "sha256:6b633b6cfee24f232d73569870f19aa86c819d750e7f3e833f2344a9eb4b4409"}, @@ -1552,21 +1552,16 @@ six = "*" [[package]] name = "django-softdelete" -version = "0.10.5" +version = "0.11.5" description = "Soft delete support for Django ORM, with undelete." optional = false python-versions = "*" groups = ["main"] files = [ - {file = "django-softdelete-0.10.5.tar.gz", hash = "sha256:7b1a31c889ed482b42c932a6d69f3653351e5699a52ebed2cfb3427a0fb7150f"}, - {file = "django_softdelete-0.10.5-py3-none-any.whl", hash = "sha256:7ddb0af2118619e7813fea4335e486d49b63ca7b0fd38d0b5ab3723cda40c577"}, + {file = "django_softdelete-0.11.5-py3-none-any.whl", hash = "sha256:72d086ea4572b85a7fe027dcf58625e091202c924c58aca2e3d4f7e6dde4c72f"}, + {file = "django_softdelete-0.11.5.tar.gz", hash = "sha256:d2e1c11cad2e664f2ec0644b9e23c7e8da4e59851fab312be69b5cf6ce12c7ab"}, ] -[package.dependencies] -setuptools = "*" -six = "*" -wheel = "*" - [[package]] name = "django-storages" version = "1.10.1" @@ -2019,8 +2014,6 @@ environs = {version = "<15", optional = true, markers = "extra == \"common-core\ gunicorn = {version = ">=19.1", optional = true, markers = "extra == \"common-core\""} prometheus-client = {version = ">=0.0.16", optional = true, markers = "extra == \"common-core\" or extra == \"task-processor\""} psycopg2-binary = {version = ">=2.9,<3", optional = true, markers = "extra == \"common-core\""} -pyfakefs = {version = ">=5,<6", optional = true, markers = "extra == \"test-tools\""} -pytest-django = {version = ">=4,<5", optional = true, markers = "extra == \"test-tools\""} requests = {version = "*", optional = true, markers = "extra == \"common-core\""} simplejson = {version = ">=3,<4", optional = true, markers = "extra == \"common-core\""} @@ -2887,16 +2880,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -4092,7 +4075,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -4100,16 +4082,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -4126,7 +4100,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -4134,7 +4107,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -5139,21 +5111,6 @@ markupsafe = ">=2.1.1" [package.extras] watchdog = ["watchdog (>=2.3)"] -[[package]] -name = "wheel" -version = "0.41.1" -description = "A built-package format for Python" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "wheel-0.41.1-py3-none-any.whl", hash = "sha256:473219bd4cbedc62cea0cb309089b593e47c15c4a2531015f94e4e3b9a0f6981"}, - {file = "wheel-0.41.1.tar.gz", hash = "sha256:12b911f083e876e10c595779709f8a88a59f45aacc646492a67fe9ef796c1b47"}, -] - -[package.extras] -test = ["pytest (>=6.0.0)", "setuptools (>=65)"] - [[package]] name = "whitenoise" version = "6.0.0" @@ -5329,4 +5286,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">3.11,<3.13" -content-hash = "4446acda8d9a50e1b59a68ce1721a282c99d5f1cb1189795f9df32d3f1edaf7c" +content-hash = "339f191acb6e99a53f7f53adda39699982543182301aa8e47088bed0ef87d7bb" From 80de6a181e050e110de9b4973f6271db9d8e0aab Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 21 Jan 2026 15:51:06 +0100 Subject: [PATCH 20/25] update django-debug-toolbar --- api/poetry.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index 166c4ce5734d..e89aa3e3be94 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1349,19 +1349,19 @@ Django = ">=2.2" [[package]] name = "django-debug-toolbar" -version = "3.2.4" +version = "6.2.0" description = "A configurable set of panels that display various debug information about the current request/response." optional = false -python-versions = ">=3.6" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "django-debug-toolbar-3.2.4.tar.gz", hash = "sha256:644bbd5c428d3283aa9115722471769cac1bec189edf3a0c855fd8ff870375a9"}, - {file = "django_debug_toolbar-3.2.4-py3-none-any.whl", hash = "sha256:6b633b6cfee24f232d73569870f19aa86c819d750e7f3e833f2344a9eb4b4409"}, + {file = "django_debug_toolbar-6.2.0-py3-none-any.whl", hash = "sha256:1575461954e6befa720e999dec13fe4f1cc8baf40b6c3ac2aec5f340c0f9c85f"}, + {file = "django_debug_toolbar-6.2.0.tar.gz", hash = "sha256:dc1c174d8fb0ea01435e02d9ceef735cf62daf37c1a6a5692d33b4127327679b"}, ] [package.dependencies] -Django = ">=2.2" -sqlparse = ">=0.2.0" +django = ">=4.2.9" +sqlparse = ">=0.2" [[package]] name = "django-environ" From 67f730355656f65f5c86e55dc7436d4a122d05f0 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 21 Jan 2026 18:47:48 +0100 Subject: [PATCH 21/25] Revert change to flagsmith-common --- api/poetry.lock | 4 +++- api/pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index e89aa3e3be94..b302b45e42c7 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -2014,6 +2014,8 @@ environs = {version = "<15", optional = true, markers = "extra == \"common-core\ gunicorn = {version = ">=19.1", optional = true, markers = "extra == \"common-core\""} prometheus-client = {version = ">=0.0.16", optional = true, markers = "extra == \"common-core\" or extra == \"task-processor\""} psycopg2-binary = {version = ">=2.9,<3", optional = true, markers = "extra == \"common-core\""} +pyfakefs = {version = ">=5,<6", optional = true, markers = "extra == \"test-tools\""} +pytest-django = {version = ">=4,<5", optional = true, markers = "extra == \"test-tools\""} requests = {version = "*", optional = true, markers = "extra == \"common-core\""} simplejson = {version = ">=3,<4", optional = true, markers = "extra == \"common-core\""} @@ -5286,4 +5288,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">3.11,<3.13" -content-hash = "339f191acb6e99a53f7f53adda39699982543182301aa8e47088bed0ef87d7bb" +content-hash = "0bb3b3343eb02e2d63cd01e5e3d36d08b7d230758aa2ecf4421d487a31db0f16" diff --git a/api/pyproject.toml b/api/pyproject.toml index 3cd94729cc5b..822730852525 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -239,7 +239,7 @@ types-psycopg2 = "^2.9.21.20250121" types-python-dateutil = "^2.9.0.20241206" types-pytz = "^2025.1.0.20250204" ruff = "^0.9.7" -flagsmith-common = "^3.1.0" +flagsmith-common = { version = "*", extras = ["test-tools"] } pytest-responses = "^0.5.1" diff-cover = "^10.1.0" django-debug-toolbar = "*" From 5e485fa8ab6d7f047f043264b874e0613eb0a877 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 21 Jan 2026 18:53:03 +0100 Subject: [PATCH 22/25] Bump djoser again... --- api/poetry.lock | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index b302b45e42c7..a5c3c08849ed 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1624,18 +1624,6 @@ files = [ django = "*" typing-extensions = "*" -[[package]] -name = "django-templated-mail" -version = "1.1.1" -description = "Send emails using Django template system." -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "django-templated-mail-1.1.1.tar.gz", hash = "sha256:8db807effebb42a532622e2d142dfd453dafcd0d7794c4c3332acb90656315f9"}, - {file = "django_templated_mail-1.1.1-py3-none-any.whl", hash = "sha256:f7127e1e31d7cad4e6c4b4801d25814d4b8782627ead76f4a75b3b7650687556"}, -] - [[package]] name = "django-test-migrations" version = "1.5.0" @@ -1765,19 +1753,19 @@ markdown = ["types-Markdown (>=0.1.5)"] [[package]] name = "djoser" -version = "2.3.0" +version = "2.3.3" description = "REST implementation of Django authentication system." optional = false -python-versions = "<4.0,>=3.8" +python-versions = "<4.0,>=3.9" groups = ["main"] files = [ - {file = "djoser-2.3.0-py3-none-any.whl", hash = "sha256:6af76cb8d73f8a879ab417c1251f5aa0242c4e3c4a8fee3a6393f1126874b269"}, - {file = "djoser-2.3.0.tar.gz", hash = "sha256:c46e4b609348b824ba138aba914ca8a89bb42b7d23975c34e2702ea04b13c989"}, + {file = "djoser-2.3.3-py3-none-any.whl", hash = "sha256:b97d233b626c26ebccb09f5614420873ad78b8b1fb1459c76475b05319bae567"}, + {file = "djoser-2.3.3.tar.gz", hash = "sha256:6ceeea9898cbdd585f1daa1ee9d46270600c0401dcd2d1db6f7894782006f6a6"}, ] [package.dependencies] -django = ">=3.0.0" -django-templated-mail = ">=1.1.1,<2.0.0" +django = ">=3.2" +djangorestframework = ">=3.14" djangorestframework-simplejwt = ">=5.0,<6.0" social-auth-app-django = ">=5.0.0,<6.0.0" From 211fc8fe1a771f3b7b0ff006f3d349cecd153d0d Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 21 Jan 2026 20:11:35 +0100 Subject: [PATCH 23/25] temp: use auth-controller branch --- api/poetry.lock | 16 ++++++++-------- api/pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index a5c3c08849ed..90df07c9b844 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1453,18 +1453,18 @@ Django = ">=3.2" [[package]] name = "django-multiselectfield" -version = "0.1.12" +version = "1.0.1" description = "Django multiple select field" optional = false python-versions = "*" groups = ["auth-controller"] files = [ - {file = "django-multiselectfield-0.1.12.tar.gz", hash = "sha256:d0a4c71568fb2332c71478ffac9f8708e01314a35cf923dfd7a191343452f9f9"}, - {file = "django_multiselectfield-0.1.12-py3-none-any.whl", hash = "sha256:c270faa7f80588214c55f2d68cbddb2add525c2aa830c216b8a198de914eb470"}, + {file = "django_multiselectfield-1.0.1-py3-none-any.whl", hash = "sha256:18dc14801f7eca844a48e21cba6d8ec35b9b581f2373bbb2cb75e6994518259a"}, + {file = "django_multiselectfield-1.0.1.tar.gz", hash = "sha256:3f8b4fff3e07d4a91c8bb4b809bc35caeb22b41769b606f4c9edc53b8d72a667"}, ] [package.dependencies] -django = ">=1.4" +django = ">=3.2" [[package]] name = "django-ordered-model" @@ -1970,13 +1970,13 @@ files = [] develop = false [package.dependencies] -django-multiselectfield = "0.1.12" +django-multiselectfield = ">=1.0.1,<2" [package.source] type = "git" url = "https://github.com/flagsmith/flagsmith-auth-controller" -reference = "v0.1.4" -resolved_reference = "f5dfd44935c41bb6d777deb06c7e06b3e44210ad" +reference = "deps/support-django-5" +resolved_reference = "1675f159cd127465580bf716a4398eb2a0c4e3a1" [[package]] name = "flagsmith-common" @@ -5276,4 +5276,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">3.11,<3.13" -content-hash = "0bb3b3343eb02e2d63cd01e5e3d36d08b7d230758aa2ecf4421d487a31db0f16" +content-hash = "72dbf4f3b3548cd029f61ef80b3f7812f99b4ff064f5e9f23107922bfc274901" diff --git a/api/pyproject.toml b/api/pyproject.toml index 822730852525..dea74680d8e2 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -173,7 +173,7 @@ django_cockroachdb = "~4.2" optional = true [tool.poetry.group.auth-controller.dependencies] -flagsmith-auth-controller = { git = "https://github.com/flagsmith/flagsmith-auth-controller", tag = "v0.1.4" } +flagsmith-auth-controller = { git = "https://github.com/flagsmith/flagsmith-auth-controller", branch = "deps/support-django-5" } [tool.poetry.group.saml] optional = true From fefa9f2e2d49f51f974bb65e820df072bf215756 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Wed, 21 Jan 2026 22:26:56 +0100 Subject: [PATCH 24/25] temp: update auth-controller to latest commit --- api/poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/poetry.lock b/api/poetry.lock index d79afa3729fb..5e9d44e1f893 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1976,7 +1976,7 @@ django-multiselectfield = ">=1.0.1,<2" type = "git" url = "https://github.com/flagsmith/flagsmith-auth-controller" reference = "deps/support-django-5" -resolved_reference = "1675f159cd127465580bf716a4398eb2a0c4e3a1" +resolved_reference = "08877b230160cb02435cb5af7591058afde7643c" [[package]] name = "flagsmith-common" From db8253051a342acc96a83e0e094e0cbabb4f3971 Mon Sep 17 00:00:00 2001 From: Matthew Elwell Date: Mon, 2 Feb 2026 15:12:06 +0000 Subject: [PATCH 25/25] use flagsmith-auth-controller==0.2.0 --- api/poetry.lock | 6 +++--- api/pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index 5a137bbe708b..64f640095a6e 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1975,8 +1975,8 @@ django-multiselectfield = ">=1.0.1,<2" [package.source] type = "git" url = "https://github.com/flagsmith/flagsmith-auth-controller" -reference = "deps/support-django-5" -resolved_reference = "08877b230160cb02435cb5af7591058afde7643c" +reference = "v0.2.0" +resolved_reference = "b7fa1f42c333b443763548ea1fe0054f07cdf641" [[package]] name = "flagsmith-common" @@ -5326,4 +5326,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">3.11,<3.13" -content-hash = "ee07277fa35935cb78666020c68b974a7deb3bce5eb990b4217117d87c71aba4" +content-hash = "dc124122fbec690a393443bd3b099130f2aa23c1bdc20f03ad7ced83f549f8c4" diff --git a/api/pyproject.toml b/api/pyproject.toml index ae66ac5e1510..2ece2a513d5f 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -175,7 +175,7 @@ django_cockroachdb = "~4.2" optional = true [tool.poetry.group.auth-controller.dependencies] -flagsmith-auth-controller = { git = "https://github.com/flagsmith/flagsmith-auth-controller", branch = "deps/support-django-5" } +flagsmith-auth-controller = { git = "https://github.com/flagsmith/flagsmith-auth-controller", tag = "v0.2.0" } [tool.poetry.group.saml] optional = true