diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index f49a0e9a..85f3bd33 100644 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -3,13 +3,13 @@ set -eux # Install django-mongodb-backend -/opt/python/3.10/bin/python3 -m venv venv +/opt/python/3.12/bin/python3 -m venv venv . venv/bin/activate python -m pip install -U pip pip install -e . # Install django and test dependencies -git clone --branch mongodb-5.2.x https://github.com/mongodb-forks/django django_repo +git clone --branch mongodb-6.0.x https://github.com/mongodb-forks/django django_repo pushd django_repo/tests/ pip install -e .. pip install -r requirements/py3.txt diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index dcf46d45..4bf0fa77 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -17,7 +17,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.12' cache: 'pip' cache-dependency-path: 'pyproject.toml' - name: Install Python dependencies @@ -37,7 +37,7 @@ jobs: with: cache: 'pip' cache-dependency-path: 'pyproject.toml' - python-version: '3.10' + python-version: '3.12' - name: Install dependencies run: | pip install -U pip diff --git a/.github/workflows/test-python-atlas.yml b/.github/workflows/test-python-atlas.yml index 175dfe18..1509c014 100644 --- a/.github/workflows/test-python-atlas.yml +++ b/.github/workflows/test-python-atlas.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v4 with: repository: 'mongodb-forks/django' - ref: 'mongodb-5.2.x' + ref: 'mongodb-6.0.x' path: 'django_repo' persist-credentials: false - name: Install system packages for Django's Python test dependencies diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml index d3fccebd..5f912bba 100644 --- a/.github/workflows/test-python.yml +++ b/.github/workflows/test-python.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v4 with: repository: 'mongodb-forks/django' - ref: 'mongodb-5.2.x' + ref: 'mongodb-6.0.x' path: 'django_repo' persist-credentials: false - name: Install system packages for Django's Python test dependencies diff --git a/.readthedocs.yaml b/.readthedocs.yaml index bf5dba2f..ba11945f 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -21,6 +21,6 @@ python: - docs build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: - python: "3.11" + python: "3.12" diff --git a/django_mongodb_backend/__init__.py b/django_mongodb_backend/__init__.py index 00700421..f57b3e7c 100644 --- a/django_mongodb_backend/__init__.py +++ b/django_mongodb_backend/__init__.py @@ -1,4 +1,4 @@ -__version__ = "5.2.0b2.dev0" +__version__ = "6.0.0b0.dev0" # Check Django compatibility before other imports which may fail if the # wrong version of Django is installed. diff --git a/django_mongodb_backend/aggregates.py b/django_mongodb_backend/aggregates.py index e67cefa2..93df6be0 100644 --- a/django_mongodb_backend/aggregates.py +++ b/django_mongodb_backend/aggregates.py @@ -1,5 +1,12 @@ -from django.db.models.aggregates import Aggregate, Count, StdDev, Variance -from django.db.models.expressions import Case, Value, When +from django.db.models.aggregates import ( + Aggregate, + AggregateFilter, + Count, + StdDev, + StringAgg, + Variance, +) +from django.db.models.expressions import Case, Col, Value, When from django.db.models.lookups import IsNull from .query_utils import process_lhs @@ -16,7 +23,11 @@ def aggregate( resolve_inner_expression=False, **extra_context, # noqa: ARG001 ): - if self.filter: + # TODO: isinstance(self.filter, Col) works around failure of + # aggregation.tests.AggregateTestCase.test_distinct_on_aggregate. Is this + # correct? + if self.filter is not None and not isinstance(self.filter, Col): + # Generate a CASE statement for this aggregate. node = self.copy() node.filter = None source_expressions = node.get_source_expressions() @@ -31,6 +42,10 @@ def aggregate( return {f"${operator}": lhs_mql} +def aggregate_filter(self, compiler, connection, **extra_context): + return self.condition.as_mql(compiler, connection, **extra_context) + + def count(self, compiler, connection, resolve_inner_expression=False, **extra_context): # noqa: ARG001 """ When resolve_inner_expression=True, return the MQL that resolves as a @@ -72,8 +87,16 @@ def stddev_variance(self, compiler, connection, **extra_context): return aggregate(self, compiler, connection, operator=operator, **extra_context) +def string_agg(self, compiler, connection, **extra_context): # # noqa: ARG001 + from django.db import NotSupportedError + + raise NotSupportedError("StringAgg is not supported.") + + def register_aggregates(): Aggregate.as_mql = aggregate + AggregateFilter.as_mql = aggregate_filter Count.as_mql = count StdDev.as_mql = stddev_variance + StringAgg.as_mql = string_agg Variance.as_mql = stddev_variance diff --git a/django_mongodb_backend/cache.py b/django_mongodb_backend/cache.py index 00b903af..ae4809a2 100644 --- a/django_mongodb_backend/cache.py +++ b/django_mongodb_backend/cache.py @@ -1,5 +1,5 @@ import pickle -from datetime import datetime, timezone +from datetime import UTC, datetime from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache from django.core.cache.backends.db import Options @@ -72,7 +72,7 @@ def get_backend_timeout(self, timeout=DEFAULT_TIMEOUT): if timeout is None: return datetime.max timestamp = super().get_backend_timeout(timeout) - return datetime.fromtimestamp(timestamp, tz=timezone.utc) + return datetime.fromtimestamp(timestamp, tz=UTC) def get(self, key, default=None, version=None): return self.get_many([key], version).get(key, default) diff --git a/django_mongodb_backend/features.py b/django_mongodb_backend/features.py index fa73461d..63f5ce0c 100644 --- a/django_mongodb_backend/features.py +++ b/django_mongodb_backend/features.py @@ -86,6 +86,21 @@ class DatabaseFeatures(BaseDatabaseFeatures): # Value.as_mql() doesn't call output_field.get_db_prep_save(): # https://github.com/mongodb/django-mongodb-backend/issues/282 "model_fields.test_jsonfield.TestSaveLoad.test_bulk_update_custom_get_prep_value", + # This backend overrides DatabaseCreation.create_test_db() so the + # deprecation warnings stacklevel points to the wrong file. + "backends.base.test_creation.TestDbCreationTests.test_serialize_deprecation", + # StringAgg is not supported. + "aggregation.tests.AggregateTestCase.test_distinct_on_stringagg", + "aggregation.tests.AggregateTestCase.test_string_agg_escapes_delimiter", + "aggregation.tests.AggregateTestCase.test_string_agg_filter", + "aggregation.tests.AggregateTestCase.test_string_agg_filter_in_subquery", + "aggregation.tests.AggregateTestCase.test_stringagg_default_value", + # bulk_create() population of _order not implemented. + # https://github.com/django/django/commit/953095d1e603fe0f8f01175b1409ca23818dcff9 + "contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_allows_duplicate_order_values", + "contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_mixed_scenario", + "contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_respects_mixed_manual_order", + "contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_with_existing_children", } # $bitAnd, #bitOr, and $bitXor are new in MongoDB 6.3. _django_test_expected_failures_bitwise = { diff --git a/django_mongodb_backend/fields/json.py b/django_mongodb_backend/fields/json.py index b7cf49dc..24c640b4 100644 --- a/django_mongodb_backend/fields/json.py +++ b/django_mongodb_backend/fields/json.py @@ -24,7 +24,12 @@ def build_json_mql_path(lhs, key_transforms): get_field = {"$getField": {"input": result, "field": key}} # Handle array indexing if the key is a digit. If key is something # like '001', it's not an array index despite isdigit() returning True. - if key.isdigit() and str(int(key)) == key: + try: + int(key) + is_digit = str(int(key)) == key + except ValueError: + is_digit = False + if is_digit: result = { "$cond": { "if": {"$isArray": result}, diff --git a/pyproject.toml b/pyproject.toml index 7b412e1d..8f100a98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version", "dependencies"] description = "Django MongoDB Backend" readme = "README.md" license = {file="LICENSE"} -requires-python = ">=3.10" +requires-python = ">=3.12" authors = [ { name = "The MongoDB Python Team" }, ] @@ -27,8 +27,6 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ]