Skip to content

Commit 9c186c3

Browse files
committed
INTPYTHON-795 Update to Django 6.0
- Adapt aggregation for AggregateFilter django/django@4b977a5 - Mark StringAgg as not supported - Fix JONField negative indexing
1 parent cbd36b5 commit 9c186c3

File tree

25 files changed

+157
-60
lines changed

25 files changed

+157
-60
lines changed

.evergreen/run-tests.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
set -eux
44

55
# Install django-mongodb-backend
6-
/opt/python/3.10/bin/python3 -m venv venv
6+
/opt/python/3.12/bin/python3 -m venv venv
77
. venv/bin/activate
88
python -m pip install -U pip
99
pip install -e .
1010

1111
# Install django and test dependencies
12-
git clone --branch mongodb-5.2.x https://github.com/mongodb-forks/django django_repo
12+
git clone --branch mongodb-6.0.x https://github.com/mongodb-forks/django django_repo
1313
pushd django_repo/tests/
1414
pip install -e ..
1515
pip install -r requirements/py3.txt

.github/workflows/linters.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
persist-credentials: false
1818
- uses: actions/setup-python@v6
1919
with:
20-
python-version: '3.10'
20+
python-version: '3.12'
2121
cache: 'pip'
2222
cache-dependency-path: 'pyproject.toml'
2323
- name: Install Python dependencies
@@ -39,7 +39,7 @@ jobs:
3939
with:
4040
cache: 'pip'
4141
cache-dependency-path: 'pyproject.toml'
42-
python-version: '3.10'
42+
python-version: '3.12'
4343
- name: Install dependencies
4444
run: |
4545
pip install -U pip

.github/workflows/test-python-atlas.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
uses: actions/checkout@v6
3434
with:
3535
repository: 'mongodb-forks/django'
36-
ref: 'mongodb-5.2.x'
36+
ref: 'mongodb-6.0.x'
3737
path: 'django_repo'
3838
persist-credentials: false
3939
- name: Install system packages for Django's Python test dependencies

.github/workflows/test-python-geo.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
uses: actions/checkout@v6
3535
with:
3636
repository: 'mongodb-forks/django'
37-
ref: 'mongodb-5.2.x'
37+
ref: 'mongodb-6.0.x'
3838
path: 'django_repo'
3939
persist-credentials: false
4040
- name: Install system packages for Django's Python test dependencies

.github/workflows/test-python.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
uses: actions/checkout@v6
3434
with:
3535
repository: 'mongodb-forks/django'
36-
ref: 'mongodb-5.2.x'
36+
ref: 'mongodb-6.0.x'
3737
path: 'django_repo'
3838
persist-credentials: false
3939
- name: Install system packages for Django's Python test dependencies

.readthedocs.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ python:
2121
- docs
2222

2323
build:
24-
os: ubuntu-22.04
24+
os: ubuntu-24.04
2525
tools:
26-
python: "3.11"
26+
python: "3.12"

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,21 @@ https://django-mongodb-backend.readthedocs.io/en/latest/.
1717
### Install
1818

1919
Use the version of `django-mongodb-backend` that corresponds to your version of
20-
Django. For example, to get the latest compatible release for Django 5.2.x:
20+
Django. For example, to get the latest compatible release for Django 6.0.x:
2121

2222
```bash
23-
pip install django-mongodb-backend==5.2.*
23+
pip install django-mongodb-backend==6.0.*
2424
```
2525

2626
### Create a project
2727

2828
From your shell, run the following command to create a new Django project
2929
called `example` using our project template. Make sure the end of the template
30-
URL corresponds to your version of Django (e.g. `5.2.x.zip` for any Django
31-
5.2.x version).
30+
URL corresponds to your version of Django (e.g. `6.0.x.zip` for any Django
31+
6.0.x version).
3232

3333
```bash
34-
django-admin startproject example --template https://github.com/mongodb-labs/django-mongodb-project/archive/refs/heads/5.2.x.zip
34+
django-admin startproject example --template https://github.com/mongodb-labs/django-mongodb-project/archive/refs/heads/6.0.x.zip
3535
```
3636

3737
You can check what version of Django you're using with:

django_mongodb_backend/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "5.2.4.dev0"
1+
__version__ = "6.0.0.dev0"
22

33
# Check Django compatibility before other imports which may fail if the
44
# wrong version of Django is installed.

django_mongodb_backend/aggregates.py

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
from django.db.models.aggregates import Aggregate, Count, StdDev, Variance
1+
from django.db import NotSupportedError
2+
from django.db.models.aggregates import (
3+
Aggregate,
4+
Count,
5+
StdDev,
6+
StringAgg,
7+
Variance,
8+
)
29
from django.db.models.expressions import Case, Value, When
310
from django.db.models.lookups import IsNull
411
from django.db.models.sql.where import WhereNode
@@ -11,13 +18,19 @@
1118

1219
def aggregate(self, compiler, connection, operator=None, resolve_inner_expression=False):
1320
agg_expression, *_ = self.get_source_expressions()
14-
if self.filter:
15-
agg_expression = Case(
16-
When(self.filter, then=agg_expression),
17-
# Skip rows that don't meet the criteria.
18-
default=Remove(),
19-
)
20-
lhs_mql = agg_expression.as_mql(compiler, connection, as_expr=True)
21+
lhs_mql = None
22+
if self.filter is not None:
23+
try:
24+
lhs_mql = self.filter.as_mql(compiler, connection, as_expr=True)
25+
except NotSupportedError:
26+
# Generate a CASE statement for this AggregateFilter.
27+
agg_expression = Case(
28+
When(self.filter.condition, then=agg_expression),
29+
# Skip rows that don't meet the criteria.
30+
default=Remove(),
31+
)
32+
if lhs_mql is None:
33+
lhs_mql = agg_expression.as_mql(compiler, connection, as_expr=True)
2134
if resolve_inner_expression:
2235
return lhs_mql
2336
operator = operator or MONGO_AGGREGATIONS.get(self.__class__, self.function.lower())
@@ -32,18 +45,30 @@ def count(self, compiler, connection, resolve_inner_expression=False):
3245
"""
3346
agg_expression, *_ = self.get_source_expressions()
3447
if not self.distinct or resolve_inner_expression:
48+
lhs_mql = None
3549
conditions = [IsNull(agg_expression, False)]
3650
if self.filter:
37-
conditions.append(self.filter)
38-
inner_expression = Case(
39-
When(WhereNode(conditions), then=agg_expression if self.distinct else Value(1)),
40-
# Skip rows that don't meet the criteria.
41-
default=Remove(),
42-
)
43-
inner_expression = inner_expression.as_mql(compiler, connection, as_expr=True)
51+
try:
52+
lhs_mql = self.filter.as_mql(compiler, connection, as_expr=True)
53+
except NotSupportedError:
54+
# Generate a CASE statement for this AggregateFilter.
55+
conditions.append(self.filter.condition)
56+
condition = When(
57+
WhereNode(conditions),
58+
then=agg_expression if self.distinct else Value(1),
59+
)
60+
inner_expression = Case(condition, default=Remove())
61+
else:
62+
inner_expression = Case(
63+
When(WhereNode(conditions), then=agg_expression if self.distinct else Value(1)),
64+
# Skip rows that don't meet the criteria.
65+
default=Remove(),
66+
)
67+
if lhs_mql is None:
68+
lhs_mql = inner_expression.as_mql(compiler, connection, as_expr=True)
4469
if resolve_inner_expression:
45-
return inner_expression
46-
return {"$sum": inner_expression}
70+
return lhs_mql
71+
return {"$sum": lhs_mql}
4772
# If distinct=True or resolve_inner_expression=False, sum the size of the
4873
# set.
4974
return {"$size": agg_expression.as_mql(compiler, connection, as_expr=True)}
@@ -57,8 +82,13 @@ def stddev_variance(self, compiler, connection):
5782
return aggregate(self, compiler, connection, operator=operator)
5883

5984

85+
def string_agg(self, compiler, connection): # noqa: ARG001
86+
raise NotSupportedError("StringAgg is not supported.")
87+
88+
6089
def register_aggregates():
6190
Aggregate.as_mql_expr = aggregate
6291
Count.as_mql_expr = count
6392
StdDev.as_mql_expr = stddev_variance
93+
StringAgg.as_mql_expr = string_agg
6494
Variance.as_mql_expr = stddev_variance

django_mongodb_backend/features.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ class DatabaseFeatures(GISFeatures, BaseDatabaseFeatures):
9999
"model_fields.test_jsonfield.TestSaveLoad.test_bulk_update_custom_get_prep_value",
100100
# To debug: https://github.com/mongodb/django-mongodb-backend/issues/362
101101
"constraints.tests.UniqueConstraintTests.test_validate_case_when",
102+
# bulk_create() population of _order doesn't work because of ObjectId
103+
# type mismatch when querying object_id CharField.
104+
# https://github.com/django/django/commit/953095d1e603fe0f8f01175b1409ca23818dcff9
105+
"contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_allows_duplicate_order_values",
106+
"contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_mixed_scenario",
107+
"contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_respects_mixed_manual_order",
108+
"contenttypes_tests.test_order_with_respect_to.OrderWithRespectToGFKTests.test_bulk_create_with_existing_children",
102109
}
103110
# $bitAnd, #bitOr, and $bitXor are new in MongoDB 6.3.
104111
_django_test_expected_failures_bitwise = {
@@ -139,6 +146,7 @@ def django_test_expected_failures(self):
139146
"validation.test_unique.PerformUniqueChecksTest.test_unique_db_default",
140147
},
141148
"Insert expressions aren't supported.": {
149+
"basic.tests.ModelTest.test_save_expressions",
142150
"bulk_create.tests.BulkCreateTests.test_bulk_insert_now",
143151
"bulk_create.tests.BulkCreateTests.test_bulk_insert_expressions",
144152
"expressions.tests.BasicExpressionsTests.test_new_object_create",
@@ -201,6 +209,7 @@ def django_test_expected_failures(self):
201209
"prefetch_related.tests.LookupOrderingTest.test_order",
202210
"prefetch_related.tests.MultiDbTests.test_using_is_honored_m2m",
203211
"prefetch_related.tests.MultiTableInheritanceTest",
212+
"prefetch_related.tests.PrefetchRelatedMTICacheTests",
204213
"prefetch_related.tests.PrefetchRelatedTests",
205214
"prefetch_related.tests.ReadPrefetchedObjectsCacheTests",
206215
"prefetch_related.tests.Ticket21410Tests",
@@ -563,6 +572,7 @@ def django_test_expected_failures(self):
563572
"Custom lookups are not supported.": {
564573
"custom_lookups.tests.BilateralTransformTests",
565574
"custom_lookups.tests.LookupTests.test_basic_lookup",
575+
"custom_lookups.tests.LookupTests.test_custom_lookup_with_subquery",
566576
"custom_lookups.tests.LookupTests.test_custom_name_lookup",
567577
"custom_lookups.tests.LookupTests.test_div3_extract",
568578
"custom_lookups.tests.SubqueryTransformTests.test_subquery_usage",
@@ -580,6 +590,16 @@ def django_test_expected_failures(self):
580590
"test_utils.tests.DisallowedDatabaseQueriesTests.test_disallowed_database_queries",
581591
"test_utils.tests.DisallowedDatabaseQueriesTests.test_disallowed_thread_database_connection",
582592
},
593+
"search lookup not supported on non-Atlas.": {
594+
"expressions.tests.BasicExpressionsTests.test_lookups_subquery",
595+
},
596+
"StringAgg is not supported.": {
597+
"aggregation.tests.AggregateTestCase.test_distinct_on_stringagg",
598+
"aggregation.tests.AggregateTestCase.test_string_agg_escapes_delimiter",
599+
"aggregation.tests.AggregateTestCase.test_string_agg_filter",
600+
"aggregation.tests.AggregateTestCase.test_string_agg_filter_in_subquery",
601+
"aggregation.tests.AggregateTestCase.test_stringagg_default_value",
602+
},
583603
}
584604

585605
@cached_property

0 commit comments

Comments
 (0)