diff --git a/docs/administration.md b/docs/administration.md index b646c1f73..fe5dd5a9b 100644 --- a/docs/administration.md +++ b/docs/administration.md @@ -179,10 +179,14 @@ following: ### Database Upgrades -In general, paperless does not require a specific version of PostgreSQL or MariaDB and it is +Paperless-ngx is compatible with Django-supported versions of PostgreSQL and MariaDB and it is generally safe to update them to newer versions. However, you should always take a backup and follow the instructions from your database's documentation for how to upgrade between major versions. +!!! note + + As of Paperless-ngx v2.18, the minimum supported version of PostgreSQL is 13. + For PostgreSQL, refer to [Upgrading a PostgreSQL Cluster](https://www.postgresql.org/docs/current/upgrading.html). For MariaDB, refer to [Upgrading MariaDB](https://mariadb.com/kb/en/upgrading/) diff --git a/pyproject.toml b/pyproject.toml index 385458aeb..ca77b74c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,22 +23,22 @@ dependencies = [ "dateparser~=1.2", # WARNING: django does not use semver. # Only patch versions are guaranteed to not introduce breaking changes. - "django~=5.1.7", + "django~=5.2.5", "django-allauth[socialaccount,mfa]~=65.4.0", - "django-auditlog~=3.1.2", + "django-auditlog~=3.2.1", "django-cachalot~=2.8.0", "django-celery-results~=2.6.0", "django-compression-middleware~=0.5.0", "django-cors-headers~=4.7.0", "django-extensions~=4.1", "django-filter~=25.1", - "django-guardian~=2.4.0", - "django-multiselectfield~=0.1.13", + "django-guardian~=3.0.3", + "django-multiselectfield~=1.0.1", "django-soft-delete~=1.0.18", "djangorestframework~=3.15", - "djangorestframework-guardian~=0.3.0", + "djangorestframework-guardian~=0.4.0", "drf-spectacular~=0.28", - "drf-spectacular-sidecar~=2025.4.1", + "drf-spectacular-sidecar~=2025.8.1", "drf-writable-nested~=0.7.1", "filelock~=3.18.0", "flower~=2.0.1", @@ -103,7 +103,7 @@ testing = [ "imagehash", "pytest~=8.4.1", "pytest-cov~=6.2.1", - "pytest-django~=4.10.0", + "pytest-django~=4.11.1", "pytest-env", "pytest-httpx", "pytest-mock", diff --git a/src/documents/management/commands/document_fuzzy_match.py b/src/documents/management/commands/document_fuzzy_match.py index 9e01ff1b0..5eebeb172 100644 --- a/src/documents/management/commands/document_fuzzy_match.py +++ b/src/documents/management/commands/document_fuzzy_match.py @@ -125,14 +125,14 @@ class Command(MultiProcessMixin, ProgressBarMixin, BaseCommand): messages.append( self.style.NOTICE( f"Document {result.doc_one_pk} fuzzy match" - f" to {result.doc_two_pk} (confidence {result.ratio:.3f})", + f" to {result.doc_two_pk} (confidence {result.ratio:.3f})\n", ), ) maybe_delete_ids.append(result.doc_two_pk) if len(messages) == 0: messages.append( - self.style.SUCCESS("No matches found"), + self.style.SUCCESS("No matches found\n"), ) self.stdout.writelines( messages, diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 5a9c089f3..33a703f96 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -2089,6 +2089,24 @@ class WorkflowTriggerSerializer(serializers.ModelSerializer): return attrs + @staticmethod + def normalize_workflow_trigger_sources(trigger): + """ + Convert sources to strings to handle django-multiselectfield v1.0 changes + """ + if trigger and "sources" in trigger: + trigger["sources"] = [ + str(s.value if hasattr(s, "value") else s) for s in trigger["sources"] + ] + + def create(self, validated_data): + WorkflowTriggerSerializer.normalize_workflow_trigger_sources(validated_data) + return super().create(validated_data) + + def update(self, instance, validated_data): + WorkflowTriggerSerializer.normalize_workflow_trigger_sources(validated_data) + return super().update(instance, validated_data) + class WorkflowActionEmailSerializer(serializers.ModelSerializer): id = serializers.IntegerField(allow_null=True, required=False) @@ -2253,6 +2271,8 @@ class WorkflowSerializer(serializers.ModelSerializer): if triggers is not None and triggers is not serializers.empty: for trigger in triggers: filter_has_tags = trigger.pop("filter_has_tags", None) + # Convert sources to strings to handle django-multiselectfield v1.0 changes + WorkflowTriggerSerializer.normalize_workflow_trigger_sources(trigger) trigger_instance, _ = WorkflowTrigger.objects.update_or_create( id=trigger.get("id"), defaults=trigger, diff --git a/src/documents/tests/test_management_exporter.py b/src/documents/tests/test_management_exporter.py index 68d204765..7415467de 100644 --- a/src/documents/tests/test_management_exporter.py +++ b/src/documents/tests/test_management_exporter.py @@ -123,7 +123,7 @@ class TestExportImport( self.trigger = WorkflowTrigger.objects.create( type=WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, - sources=[1], + sources=[str(WorkflowTrigger.DocumentSourceChoices.CONSUME_FOLDER.value)], filter_filename="*", ) self.action = WorkflowAction.objects.create(assign_title="new title") diff --git a/src/documents/tests/test_management_fuzzy.py b/src/documents/tests/test_management_fuzzy.py index 7cc1f265e..2d7d3735a 100644 --- a/src/documents/tests/test_management_fuzzy.py +++ b/src/documents/tests/test_management_fuzzy.py @@ -87,7 +87,7 @@ class TestFuzzyMatchCommand(TestCase): filename="other_test.pdf", ) stdout, _ = self.call_command() - self.assertEqual(stdout, "No matches found\n") + self.assertIn("No matches found", stdout) def test_with_matches(self): """ @@ -116,7 +116,7 @@ class TestFuzzyMatchCommand(TestCase): filename="other_test.pdf", ) stdout, _ = self.call_command("--processes", "1") - self.assertRegex(stdout, self.MSG_REGEX + "\n") + self.assertRegex(stdout, self.MSG_REGEX) def test_with_3_matches(self): """ @@ -152,11 +152,10 @@ class TestFuzzyMatchCommand(TestCase): filename="final_test.pdf", ) stdout, _ = self.call_command() - lines = [x.strip() for x in stdout.split("\n") if len(x.strip())] + lines = [x.strip() for x in stdout.splitlines() if x.strip()] self.assertEqual(len(lines), 3) - self.assertRegex(lines[0], self.MSG_REGEX) - self.assertRegex(lines[1], self.MSG_REGEX) - self.assertRegex(lines[2], self.MSG_REGEX) + for line in lines: + self.assertRegex(line, self.MSG_REGEX) def test_document_deletion(self): """ @@ -197,14 +196,12 @@ class TestFuzzyMatchCommand(TestCase): stdout, _ = self.call_command("--delete") - lines = [x.strip() for x in stdout.split("\n") if len(x.strip())] - self.assertEqual(len(lines), 3) - self.assertEqual( - lines[0], + self.assertIn( "The command is configured to delete documents. Use with caution", + stdout, ) - self.assertRegex(lines[1], self.MSG_REGEX) - self.assertEqual(lines[2], "Deleting 1 documents based on ratio matches") + self.assertRegex(stdout, self.MSG_REGEX) + self.assertIn("Deleting 1 documents based on ratio matches", stdout) self.assertEqual(Document.objects.count(), 2) self.assertIsNotNone(Document.objects.get(pk=1)) diff --git a/src/documents/tests/test_migration_workflows.py b/src/documents/tests/test_migration_workflows.py index 989518818..60e429d68 100644 --- a/src/documents/tests/test_migration_workflows.py +++ b/src/documents/tests/test_migration_workflows.py @@ -104,7 +104,7 @@ class TestReverseMigrateWorkflow(TestMigrations): trigger = WorkflowTrigger.objects.create( type=0, - sources=[DocumentSource.ConsumeFolder], + sources=[str(DocumentSource.ConsumeFolder)], filter_path="*/path/*", filter_filename="*file*", ) diff --git a/src/paperless/auth.py b/src/paperless/auth.py index 36131847f..c68d63cf0 100644 --- a/src/paperless/auth.py +++ b/src/paperless/auth.py @@ -54,7 +54,7 @@ class HttpRemoteUserMiddleware(PersistentRemoteUserMiddleware): header = settings.HTTP_REMOTE_USER_HEADER_NAME - def process_request(self, request: HttpRequest) -> None: + def __call__(self, request: HttpRequest) -> None: # If remote user auth is enabled only for the frontend, not the API, # then we need dont want to authenticate the user for API requests. if ( @@ -62,8 +62,8 @@ class HttpRemoteUserMiddleware(PersistentRemoteUserMiddleware): and "paperless.auth.PaperlessRemoteUserAuthentication" not in settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"] ): - return - return super().process_request(request) + return self.get_response(request) + return super().__call__(request) class PaperlessRemoteUserAuthentication(authentication.RemoteUserAuthentication): diff --git a/src/paperless/checks.py b/src/paperless/checks.py index 75e466d06..29d35f76b 100644 --- a/src/paperless/checks.py +++ b/src/paperless/checks.py @@ -214,31 +214,3 @@ def audit_log_check(app_configs, **kwargs): ) return result - - -@register() -def check_postgres_version(app_configs, **kwargs): - """ - Django 5.2 removed PostgreSQL 13 support and thus it will be removed in - a future Paperless-ngx version. This check can be removed eventually. - See https://docs.djangoproject.com/en/5.2/releases/5.2/#dropped-support-for-postgresql-13 - """ - db_conn = connections["default"] - result = [] - if db_conn.vendor == "postgresql": - try: - with db_conn.cursor() as cursor: - cursor.execute("SHOW server_version;") - version = cursor.fetchone()[0] - if version.startswith("13"): - return [ - Warning( - "PostgreSQL 13 is deprecated and will not be supported in a future Paperless-ngx release.", - hint="Upgrade to PostgreSQL 14 or newer.", - ), - ] - except Exception: # pragma: no cover - # Don't block checks on version query failure - pass - - return result diff --git a/src/paperless/tests/test_checks.py b/src/paperless/tests/test_checks.py index f1909112b..781956ff6 100644 --- a/src/paperless/tests/test_checks.py +++ b/src/paperless/tests/test_checks.py @@ -9,7 +9,6 @@ from documents.tests.utils import DirectoriesMixin from documents.tests.utils import FileSystemAssertsMixin from paperless.checks import audit_log_check from paperless.checks import binaries_check -from paperless.checks import check_postgres_version from paperless.checks import debug_mode_check from paperless.checks import paths_check from paperless.checks import settings_values_check @@ -263,39 +262,3 @@ class TestAuditLogChecks(TestCase): ("auditlog table was found but audit log is disabled."), msg.msg, ) - - -class TestPostgresVersionCheck(TestCase): - @mock.patch("paperless.checks.connections") - def test_postgres_13_warns(self, mock_connections): - mock_connection = mock.MagicMock() - mock_connection.vendor = "postgresql" - mock_cursor = mock.MagicMock() - mock_cursor.__enter__.return_value.fetchone.return_value = ["13.11"] - mock_connection.cursor.return_value = mock_cursor - mock_connections.__getitem__.return_value = mock_connection - - warnings = check_postgres_version(None) - self.assertEqual(len(warnings), 1) - self.assertIn("PostgreSQL 13 is deprecated", warnings[0].msg) - - @mock.patch("paperless.checks.connections") - def test_postgres_14_passes(self, mock_connections): - mock_connection = mock.MagicMock() - mock_connection.vendor = "postgresql" - mock_cursor = mock.MagicMock() - mock_cursor.__enter__.return_value.fetchone.return_value = ["14.10"] - mock_connection.cursor.return_value = mock_cursor - mock_connections.__getitem__.return_value = mock_connection - - warnings = check_postgres_version(None) - self.assertEqual(warnings, []) - - @mock.patch("paperless.checks.connections") - def test_non_postgres_skipped(self, mock_connections): - mock_connection = mock.MagicMock() - mock_connection.vendor = "sqlite" - mock_connections.__getitem__.return_value = mock_connection - - warnings = check_postgres_version(None) - self.assertEqual(warnings, []) diff --git a/src/paperless/tests/test_remote_user.py b/src/paperless/tests/test_remote_user.py index ebe1b3ff5..78b3393d5 100644 --- a/src/paperless/tests/test_remote_user.py +++ b/src/paperless/tests/test_remote_user.py @@ -1,6 +1,7 @@ import os from unittest import mock +from django.conf import settings from django.contrib.auth.models import User from django.test import override_settings from rest_framework import status @@ -91,6 +92,7 @@ class TestRemoteUser(DirectoriesMixin, APITestCase): @override_settings( REST_FRAMEWORK={ + **settings.REST_FRAMEWORK, "DEFAULT_AUTHENTICATION_CLASSES": [ "rest_framework.authentication.BasicAuthentication", "rest_framework.authentication.TokenAuthentication", diff --git a/uv.lock b/uv.lock index fe673a5b8..055fc32d5 100644 --- a/uv.lock +++ b/uv.lock @@ -626,15 +626,15 @@ wheels = [ [[package]] name = "django" -version = "5.1.8" +version = "5.2.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiref", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "sqlparse", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/40/45adc1b93435d1b418654a734b68351bb6ce0a0e5e37b2f0e9aeb1a2e233/Django-5.1.8.tar.gz", hash = "sha256:42e92a1dd2810072bcc40a39a212b693f94406d0ba0749e68eb642f31dc770b4", size = 10723602, upload-time = "2025-04-02T11:19:56.028Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/9b/779f853c3d2d58b9e08346061ff3e331cdec3fe3f53aae509e256412a593/django-5.2.5.tar.gz", hash = "sha256:0745b25681b129a77aae3d4f6549b62d3913d74407831abaa0d9021a03954bae", size = 10859748, upload-time = "2025-08-06T08:26:29.978Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/0d/e6dd0ed898b920fec35c6eeeb9acbeb831fff19ad21c5e684744df1d4a36/Django-5.1.8-py3-none-any.whl", hash = "sha256:11b28fa4b00e59d0def004e9ee012fefbb1065a5beb39ee838983fd24493ad4f", size = 8277130, upload-time = "2025-04-02T11:19:51.591Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6e/98a1d23648e0085bb5825326af17612ecd8fc76be0ce96ea4dc35e17b926/django-5.2.5-py3-none-any.whl", hash = "sha256:2b2ada0ee8a5ff743a40e2b9820d1f8e24c11bac9ae6469cd548f0057ea6ddcd", size = 8302999, upload-time = "2025-08-06T08:26:23.562Z" }, ] [[package]] @@ -660,15 +660,15 @@ socialaccount = [ [[package]] name = "django-auditlog" -version = "3.1.2" +version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/3a/61bfb180019d08db3f7a2e4097bda14ee32bd57f5dffda0c84b2d4c26304/django_auditlog-3.1.2.tar.gz", hash = "sha256:435345b4055d16abfb4ada4bf11320f9e2f6d343874464471fa0041f13f3a474", size = 69359, upload-time = "2025-04-26T11:01:56.553Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/46/9da1d94493832fa18d2f6324a76d387fa232001593866987a96047709f4e/django_auditlog-3.2.1.tar.gz", hash = "sha256:63a4c9f7793e94eed804bc31a04d9b0b58244b1d280e2ed273c8b406bff1f779", size = 72926, upload-time = "2025-07-03T20:08:17.734Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/34/47edd758abcb4426953b5ff2fa4dd9956c2304e96160ab1b95c3a1ab6e61/django_auditlog-3.1.2-py3-none-any.whl", hash = "sha256:6432a83fdf4397a726488d101fedcb62daafd6d4b825a0fc4c50e3657f5883cd", size = 37312, upload-time = "2025-04-26T11:01:16.776Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/67296d050a72dcd76f57f220df621cb27e5b9282ba7ad0f5f74870dce241/django_auditlog-3.2.1-py3-none-any.whl", hash = "sha256:99603ca9d015f7e9b062b1c34f3e0826a3ce6ae6e5950c81bb7e663f7802a899", size = 38330, upload-time = "2025-07-03T20:07:51.735Z" }, ] [[package]] @@ -764,38 +764,38 @@ wheels = [ [[package]] name = "django-guardian" -version = "2.4.0" +version = "3.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6f/4c/d1f6923a0ad7f16c403a54c09e94acb76ac6c3765e02523fb09b2b03e1a8/django-guardian-2.4.0.tar.gz", hash = "sha256:c58a68ae76922d33e6bdc0e69af1892097838de56e93e78a8361090bcd9f89a0", size = 159008, upload-time = "2021-05-23T22:11:26.23Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/c2/3ed43813dd7313f729dbaa829b4f9ed4a647530151f672cfb5f843c12edf/django_guardian-3.0.3.tar.gz", hash = "sha256:4e59eab4d836da5a027cf0c176d14bc2a4e22cbbdf753159a03946c08c8a196d", size = 85410, upload-time = "2025-06-25T20:42:17.475Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/25/869df12e544b51f583254aadbba6c1a95e11d2d08edeb9e58dd715112db5/django_guardian-2.4.0-py3-none-any.whl", hash = "sha256:440ca61358427e575323648b25f8384739e54c38b3d655c81d75e0cd0d61b697", size = 106107, upload-time = "2021-05-23T22:11:22.75Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/e6f629a978ef5fab8b8d2760cacc3e451016cef952cf4c049d672c5c6b07/django_guardian-3.0.3-py3-none-any.whl", hash = "sha256:d2164cea9f03c369d7ade21802710f3ab23ca6734bcc7dfcfb385906783916c7", size = 118198, upload-time = "2025-06-25T20:42:15.377Z" }, ] [[package]] name = "django-multiselectfield" -version = "0.1.13" +version = "1.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/c3/1a326cc669fea63f22e63f6e2b2b014534a15966506e8d7fa3c232aced42/django_multiselectfield-0.1.13.tar.gz", hash = "sha256:437d72632f4c0ca416951917632529c3d1d42b62bb6c3c03e3396fa50265be94", size = 11704, upload-time = "2024-07-01T05:40:39.456Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/9a/27060e8aa491ff2d286054df2e89df481a8dfe0e5e459fa36c0f48e3c10c/django_multiselectfield-1.0.1.tar.gz", hash = "sha256:3f8b4fff3e07d4a91c8bb4b809bc35caeb22b41769b606f4c9edc53b8d72a667", size = 22025, upload-time = "2025-06-12T14:41:21.599Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/9e/3ed6f072f1e806516dbc8c95e4ecae7b87af6757eb5d428857ea0a097e76/django_multiselectfield-0.1.13-py3-none-any.whl", hash = "sha256:f146ef568c823a409f4021b98781666ec2debabfceca9176116d749dc39cb8b3", size = 14804, upload-time = "2024-07-01T05:40:37.549Z" }, + { url = "https://files.pythonhosted.org/packages/6d/10/23c0644cf67567bbe4e3a2eeeec0e9c79b701990c0e07c5ee4a4f8897f91/django_multiselectfield-1.0.1-py3-none-any.whl", hash = "sha256:18dc14801f7eca844a48e21cba6d8ec35b9b581f2373bbb2cb75e6994518259a", size = 20481, upload-time = "2025-06-12T14:41:20.107Z" }, ] [[package]] name = "django-soft-delete" -version = "1.0.18" +version = "1.0.19" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/7e/89cba723dd5d34ccb6003f4812de7f5c69ba32bd73ab37f2bb21ff344c6c/django_soft_delete-1.0.18.tar.gz", hash = "sha256:d2f9db449a4f008e9786f82fa4bafbe4075f7a0b3284844735007e988b2a4df6", size = 11979, upload-time = "2025-02-01T13:43:53.804Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/77/44a6615a7da3ca0ddc624039d399d17d6c3503e1c2dad08b443f8d4a3570/django_soft_delete-1.0.19.tar.gz", hash = "sha256:c67ee8920e1456eca84cc59b3304ef27fa9d476b516be726ce7e1fc558502908", size = 11993, upload-time = "2025-06-19T20:32:20.373Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/d0/6dcca209e48081213854088fc7014e9dbdcd24f4ec2118f8ee29d11c8623/django_soft_delete-1.0.18-py3-none-any.whl", hash = "sha256:603a29e82bbb7a5bada69f2754fad225ccd8cd7f485320ec06d0fc4e9dfddcf0", size = 10876, upload-time = "2025-02-01T13:43:52.109Z" }, + { url = "https://files.pythonhosted.org/packages/96/9e/f8b5a02cdcba606eb40fbe30fe0c9c7493a2c18f83ec3b4620e4e86a34d3/django_soft_delete-1.0.19-py3-none-any.whl", hash = "sha256:46aa5fab513db566d3d7a832529ed27245b5900eaaa705535bc7674055801a46", size = 10889, upload-time = "2025-06-19T20:32:19.083Z" }, ] [[package]] @@ -835,28 +835,28 @@ wheels = [ [[package]] name = "djangorestframework" -version = "3.16.0" +version = "3.16.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/97/112c5a72e6917949b6d8a18ad6c6e72c46da4290c8f36ee5f1c1dcbc9901/djangorestframework-3.16.0.tar.gz", hash = "sha256:f022ff46613584de994c0c6a4aebbace5fd700555fbe9d33b865ebf173eba6c9", size = 1068408, upload-time = "2025-03-28T14:18:42.065Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/95/5376fe618646fde6899b3cdc85fd959716bb67542e273a76a80d9f326f27/djangorestframework-3.16.1.tar.gz", hash = "sha256:166809528b1aced0a17dc66c24492af18049f2c9420dbd0be29422029cfc3ff7", size = 1089735, upload-time = "2025-08-06T17:50:53.251Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/3e/2448e93f4f87fc9a9f35e73e3c05669e0edd0c2526834686e949bb1fd303/djangorestframework-3.16.0-py3-none-any.whl", hash = "sha256:bea7e9f6b96a8584c5224bfb2e4348dfb3f8b5e34edbecb98da258e892089361", size = 1067305, upload-time = "2025-03-28T14:18:39.489Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ce/bf8b9d3f415be4ac5588545b5fcdbbb841977db1c1d923f7568eeabe1689/djangorestframework-3.16.1-py3-none-any.whl", hash = "sha256:33a59f47fb9c85ede792cbf88bde71893bcda0667bc573f784649521f1102cec", size = 1080442, upload-time = "2025-08-06T17:50:50.667Z" }, ] [[package]] name = "djangorestframework-guardian" -version = "0.3.0" +version = "0.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "django-guardian", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "djangorestframework", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e5/80/0f2190bacfe7c7b2e22d0e1e695882ec3123f9e58817c8392a258cd46442/djangorestframework-guardian-0.3.0.tar.gz", hash = "sha256:1883756452d9bfcc2a51fb4e039a6837a8f6697c756447aa83af085749b59330", size = 8647, upload-time = "2019-10-14T04:24:25.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/c4/67df9963395e9dddd4e16cbf75098953798e5135f73fb8f4855895505e39/djangorestframework_guardian-0.4.0.tar.gz", hash = "sha256:a8113659e062f65b74cc31af6982420c382642e782d38581b3fdc748a179756c", size = 8239, upload-time = "2025-07-01T07:22:10.809Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/cc/35c1d8fb99172b2646f29e270e9ec443ffe09e0b63e61cd528d4fb4b8b07/djangorestframework_guardian-0.3.0-py2.py3-none-any.whl", hash = "sha256:3bd3dd6ea58e1bceca5048faf6f8b1a93bb5dcff30ba5eb91b9a0e190a48a0c7", size = 6931, upload-time = "2019-08-02T01:00:39.543Z" }, + { url = "https://files.pythonhosted.org/packages/59/81/3d62f7ff71f7c45ec6664ebf03a4c736bf77f49481604361d40f8f4471e4/djangorestframework_guardian-0.4.0-py3-none-any.whl", hash = "sha256:30c2a349318c1cd603d6953d50d58159f9a0c833f5f8f5a811407d5984a39e14", size = 6064, upload-time = "2025-07-01T07:22:09.661Z" }, ] [[package]] @@ -900,14 +900,14 @@ wheels = [ [[package]] name = "drf-spectacular-sidecar" -version = "2025.4.1" +version = "2025.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5d/b6/ce857d73b65b86a9034d0604b5dc1a002f7fa218e32c4dba479a197acd70/drf_spectacular_sidecar-2025.4.1.tar.gz", hash = "sha256:ea7dc4e674174616589d258b5c9676f3c451ec422e62b79e31234d39db53922d", size = 2402076, upload-time = "2025-04-01T11:23:30.627Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/f7/0cb2f520723f1823ef7b6651d447927f61ba92d152a5d68132599b90624f/drf_spectacular_sidecar-2025.8.1.tar.gz", hash = "sha256:1944ae0eb5136cff5aa135211bec31084cef1af03a04de9b7f2f912b3c59c251", size = 2407787, upload-time = "2025-08-01T11:28:01.319Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/c3/d2f31ef748f89d68121aa3d4a71f7dfd44ea54957b84602d70cda2491c43/drf_spectacular_sidecar-2025.4.1-py3-none-any.whl", hash = "sha256:343a24b0d03125fa76d07685072f55779c5c4124d90c10b14e315fdc143ad9b9", size = 2422415, upload-time = "2025-04-01T11:23:28.797Z" }, + { url = "https://files.pythonhosted.org/packages/1f/3b/0fcdc6eb294a11ed6e3ddc02fc29968bf403d3ce31645764eedfc91f87a6/drf_spectacular_sidecar-2025.8.1-py3-none-any.whl", hash = "sha256:c65a2a423000cc067395150b4dc28e7398a762d66ee101c4c38a4fb0d29a42a2", size = 2427849, upload-time = "2025-08-01T11:27:59.648Z" }, ] [[package]] @@ -2050,22 +2050,22 @@ requires-dist = [ { name = "channels-redis", specifier = "~=4.2" }, { name = "concurrent-log-handler", specifier = "~=0.9.25" }, { name = "dateparser", specifier = "~=1.2" }, - { name = "django", specifier = "~=5.1.7" }, + { name = "django", specifier = "~=5.2.5" }, { name = "django-allauth", extras = ["socialaccount", "mfa"], specifier = "~=65.4.0" }, - { name = "django-auditlog", specifier = "~=3.1.2" }, + { name = "django-auditlog", specifier = "~=3.2.1" }, { name = "django-cachalot", specifier = "~=2.8.0" }, { name = "django-celery-results", specifier = "~=2.6.0" }, { name = "django-compression-middleware", specifier = "~=0.5.0" }, { name = "django-cors-headers", specifier = "~=4.7.0" }, { name = "django-extensions", specifier = "~=4.1" }, { name = "django-filter", specifier = "~=25.1" }, - { name = "django-guardian", specifier = "~=2.4.0" }, - { name = "django-multiselectfield", specifier = "~=0.1.13" }, + { name = "django-guardian", specifier = "~=3.0.3" }, + { name = "django-multiselectfield", specifier = "~=1.0.1" }, { name = "django-soft-delete", specifier = "~=1.0.18" }, { name = "djangorestframework", specifier = "~=3.15" }, - { name = "djangorestframework-guardian", specifier = "~=0.3.0" }, + { name = "djangorestframework-guardian", specifier = "~=0.4.0" }, { name = "drf-spectacular", specifier = "~=0.28" }, - { name = "drf-spectacular-sidecar", specifier = "~=2025.4.1" }, + { name = "drf-spectacular-sidecar", specifier = "~=2025.8.1" }, { name = "drf-writable-nested", specifier = "~=0.7.1" }, { name = "filelock", specifier = "~=3.18.0" }, { name = "flower", specifier = "~=2.0.1" }, @@ -2119,7 +2119,7 @@ dev = [ { name = "pre-commit-uv", specifier = "~=4.1.3" }, { name = "pytest", specifier = "~=8.4.1" }, { name = "pytest-cov", specifier = "~=6.2.1" }, - { name = "pytest-django", specifier = "~=4.10.0" }, + { name = "pytest-django", specifier = "~=4.11.1" }, { name = "pytest-env" }, { name = "pytest-httpx" }, { name = "pytest-mock" }, @@ -2143,7 +2143,7 @@ testing = [ { name = "imagehash" }, { name = "pytest", specifier = "~=8.4.1" }, { name = "pytest-cov", specifier = "~=6.2.1" }, - { name = "pytest-django", specifier = "~=4.10.0" }, + { name = "pytest-django", specifier = "~=4.11.1" }, { name = "pytest-env" }, { name = "pytest-httpx" }, { name = "pytest-mock" }, @@ -2597,14 +2597,14 @@ wheels = [ [[package]] name = "pytest-django" -version = "4.10.0" +version = "4.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/10/a096573b4b896f18a8390d9dafaffc054c1f613c60bf838300732e538890/pytest_django-4.10.0.tar.gz", hash = "sha256:1091b20ea1491fd04a310fc9aaff4c01b4e8450e3b157687625e16a6b5f3a366", size = 84710, upload-time = "2025-02-10T14:52:57.337Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/fb/55d580352db26eb3d59ad50c64321ddfe228d3d8ac107db05387a2fadf3a/pytest_django-4.11.1.tar.gz", hash = "sha256:a949141a1ee103cb0e7a20f1451d355f83f5e4a5d07bdd4dcfdd1fd0ff227991", size = 86202, upload-time = "2025-04-03T18:56:09.338Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/4c/a4fe18205926216e1aebe1f125cba5bce444f91b6e4de4f49fa87e322775/pytest_django-4.10.0-py3-none-any.whl", hash = "sha256:57c74ef3aa9d89cae5a5d73fbb69a720a62673ade7ff13b9491872409a3f5918", size = 23975, upload-time = "2025-02-10T14:52:55.325Z" }, + { url = "https://files.pythonhosted.org/packages/be/ac/bd0608d229ec808e51a21044f3f2f27b9a37e7a0ebaca7247882e67876af/pytest_django-4.11.1-py3-none-any.whl", hash = "sha256:1b63773f648aa3d8541000c26929c1ea63934be1cfa674c76436966d73fe6a10", size = 25281, upload-time = "2025-04-03T18:56:07.678Z" }, ] [[package]]