From f87e418d577670962f9a8969ca951f9176f00998 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 8 Jul 2024 11:01:58 -0400 Subject: [PATCH] update to Django 5.1 --- .evergreen/run-tests.sh | 2 +- .github/workflows/test-python.yml | 2 +- README.md | 10 +-- django_mongodb_backend/__init__.py | 2 +- django_mongodb_backend/compiler.py | 2 +- django_mongodb_backend/features.py | 13 +++ django_mongodb_backend/query_utils.py | 2 + django_mongodb_backend/schema.py | 18 +--- docs/source/conf.py | 4 +- docs/source/index.rst | 2 +- pyproject.toml | 2 +- requirements.txt | 3 +- tests/schema_/test_embedded_model.py | 115 +------------------------- 13 files changed, 32 insertions(+), 145 deletions(-) diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index 147f40ca..f383a71a 100644 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -9,7 +9,7 @@ python -m pip install -U pip pip install -e . # Install django and test dependencies -git clone --branch mongodb-5.0.x https://github.com/mongodb-forks/django django_repo +git clone --branch mongodb-5.1.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/test-python.yml b/.github/workflows/test-python.yml index 5aadeac0..cc791d55 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.0.x' + ref: 'mongodb-5.1.x' path: 'django_repo' persist-credentials: false - name: Install system packages for Django's Python test dependencies diff --git a/README.md b/README.md index cea132b0..9b712643 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ explore and build. The best way to share this is via our [MongoDB Community Foru ## Install Use the version of `django-mongodb-backend` that corresponds to your version of -Django. For example, to get the latest compatible release for Django 5.0.x: +Django. For example, to get the latest compatible release for Django 5.1.x: ```bash -$ pip install --pre django-mongodb-backend==5.0.* +$ pip install --pre django-mongodb-backend==5.1.* ``` (Until the package is out of beta, you must use pip's `--pre` option.) @@ -21,11 +21,11 @@ $ pip install --pre django-mongodb-backend==5.0.* From your shell, run the following command to create a new Django project called `example` using our custom template. Make sure the zipfile referenced at the end of the template link corresponds to your -version of Django. The snippet below specifies `5.0.x.zip` at the end of -the template url to get the template for any Django version matching 5.0: +version of Django. The snippet below specifies `5.1.x.zip` at the end of +the template url to get the template for any Django version matching 5.1: ```bash -$ django-admin startproject example --template https://github.com/mongodb-labs/django-mongodb-project/archive/refs/heads/5.0.x.zip +$ django-admin startproject example --template https://github.com/mongodb-labs/django-mongodb-project/archive/refs/heads/5.1.x.zip ``` diff --git a/django_mongodb_backend/__init__.py b/django_mongodb_backend/__init__.py index 4810c363..8662e138 100644 --- a/django_mongodb_backend/__init__.py +++ b/django_mongodb_backend/__init__.py @@ -1,4 +1,4 @@ -__version__ = "5.0.0a3" +__version__ = "5.1.0a1" # Check Django compatibility before other imports which may fail if the # wrong version of Django is installed. diff --git a/django_mongodb_backend/compiler.py b/django_mongodb_backend/compiler.py index c4ce0ab4..cf666619 100644 --- a/django_mongodb_backend/compiler.py +++ b/django_mongodb_backend/compiler.py @@ -91,7 +91,7 @@ def _prepare_expressions_for_pipeline(self, expression, target, annotation_group rhs = sub_expr.as_mql(self, self.connection, resolve_inner_expression=True) group[alias] = {"$addToSet": rhs} replacing_expr = sub_expr.copy() - replacing_expr.set_source_expressions([inner_column]) + replacing_expr.set_source_expressions([inner_column, None]) else: group[alias] = sub_expr.as_mql(self, self.connection) replacing_expr = inner_column diff --git a/django_mongodb_backend/features.py b/django_mongodb_backend/features.py index 303393b2..bfb80b05 100644 --- a/django_mongodb_backend/features.py +++ b/django_mongodb_backend/features.py @@ -40,6 +40,12 @@ class DatabaseFeatures(BaseDatabaseFeatures): uses_savepoints = False _django_test_expected_failures = { + # $concat only supports strings, not int + "db_functions.text.test_concat.ConcatTests.test_concat_non_str", + # QuerySet.order_by() with annotation transform doesn't work: + # "Expression $mod takes exactly 2 arguments. 1 were passed in" + # https://github.com/django/django/commit/b0ad41198b3e333f57351e3fce5a1fb47f23f376 + "aggregation.tests.AggregateTestCase.test_order_by_aggregate_transform", # 'NulledTransform' object has no attribute 'as_mql'. "lookup.tests.LookupTests.test_exact_none_transform", # "Save with update_fields did not affect any rows." @@ -70,6 +76,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): # Connection creation doesn't follow the usual Django API. "backends.tests.ThreadTests.test_pass_connection_between_threads", "backends.tests.ThreadTests.test_default_connection_thread_local", + "test_utils.tests.DisallowedDatabaseQueriesTests.test_disallowed_thread_database_connection", # Object of type ObjectId is not JSON serializable. "auth_tests.test_views.LoginTest.test_login_session_without_hash_session_key", # GenericRelation.value_to_string() assumes integer pk. @@ -164,6 +171,7 @@ def django_test_expected_failures(self): "fixtures.tests.FixtureLoadingTests.test_loading_and_dumping", "m2m_through_regress.test_multitable.MultiTableTests.test_m2m_prefetch_proxied", "m2m_through_regress.test_multitable.MultiTableTests.test_m2m_prefetch_reverse_proxied", + "many_to_many.tests.ManyToManyQueryTests.test_prefetch_related_no_queries_optimization_disabled", "many_to_many.tests.ManyToManyTests.test_add_after_prefetch", "many_to_many.tests.ManyToManyTests.test_add_then_remove_after_prefetch", "many_to_many.tests.ManyToManyTests.test_clear_after_prefetch", @@ -375,7 +383,11 @@ def django_test_expected_failures(self): "delete.tests.DeletionTests.test_only_referenced_fields_selected", "expressions.tests.ExistsTests.test_optimizations", "lookup.tests.LookupTests.test_in_ignore_none", + "lookup.tests.LookupTests.test_lookup_direct_value_rhs_unwrapped", "lookup.tests.LookupTests.test_textfield_exact_null", + "many_to_many.tests.ManyToManyQueryTests.test_count_join_optimization_disabled", + "many_to_many.tests.ManyToManyQueryTests.test_exists_join_optimization_disabled", + "many_to_many.tests.ManyToManyTests.test_custom_default_manager_exists_count", "migrations.test_commands.MigrateTests.test_migrate_syncdb_app_label", "migrations.test_commands.MigrateTests.test_migrate_syncdb_deferred_sql_executed_with_schemaeditor", "queries.tests.ExistsSql.test_exists", @@ -423,6 +435,7 @@ def django_test_expected_failures(self): "raw_query.tests.RawQueryTests", "schema.test_logging.SchemaLoggerTests.test_extra_args", "schema.tests.SchemaTests.test_remove_constraints_capital_letters", + "test_utils.tests.AllowedDatabaseQueriesTests.test_allowed_database_copy_queries", "timezones.tests.LegacyDatabaseTests.test_cursor_execute_accepts_naive_datetime", "timezones.tests.LegacyDatabaseTests.test_cursor_execute_returns_naive_datetime", "timezones.tests.LegacyDatabaseTests.test_raw_sql", diff --git a/django_mongodb_backend/query_utils.py b/django_mongodb_backend/query_utils.py index dd7042c7..670c2c9f 100644 --- a/django_mongodb_backend/query_utils.py +++ b/django_mongodb_backend/query_utils.py @@ -12,6 +12,8 @@ def process_lhs(node, compiler, connection): # node is a Func or Expression, possibly with multiple source expressions. result = [] for expr in node.get_source_expressions(): + if expr is None: + continue try: result.append(expr.as_mql(compiler, connection)) except FullResultSet: diff --git a/django_mongodb_backend/schema.py b/django_mongodb_backend/schema.py index f04d0681..cea89ae2 100644 --- a/django_mongodb_backend/schema.py +++ b/django_mongodb_backend/schema.py @@ -51,8 +51,8 @@ def create_model(self, model): def _create_model_indexes(self, model, column_prefix="", parent_model=None): """ - Create all indexes (field indexes & uniques, Meta.index_together, - Meta.unique_together, Meta.constraints, Meta.indexes) for the model. + Create all indexes (field indexes & uniques, Meta.unique_together, + Meta.constraints, Meta.indexes) for the model. If this is a recursive call due to an embedded model, `column_prefix` tracks the path that must be prepended to the index's column, and @@ -71,11 +71,6 @@ def _create_model_indexes(self, model, column_prefix="", parent_model=None): self._add_field_index(parent_model or model, field, column_prefix=column_prefix) elif self._field_should_have_unique(field): self._add_field_unique(parent_model or model, field, column_prefix=column_prefix) - # Meta.index_together (RemovedInDjango51Warning) - for field_names in model._meta.index_together: - self._add_composed_index( - model, field_names, column_prefix=column_prefix, parent_model=parent_model - ) # Meta.unique_together if model._meta.unique_together: self.alter_unique_together( @@ -208,15 +203,6 @@ def _remove_model_indexes(self, model, column_prefix="", parent_model=None): self._remove_field_index(parent_model or model, field, column_prefix=column_prefix) elif self._field_should_have_unique(field): self._remove_field_unique(parent_model or model, field, column_prefix=column_prefix) - # Meta.index_together (RemovedInDjango51Warning) - for field_names in model._meta.index_together: - self._remove_composed_index( - model, - field_names, - {"index": True, "unique": False}, - column_prefix=column_prefix, - parent_model=parent_model, - ) # Meta.unique_together if model._meta.unique_together: self.alter_unique_together( diff --git a/docs/source/conf.py b/docs/source/conf.py index 55bb84e1..4a10dc8e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -38,8 +38,8 @@ intersphinx_mapping = { "django": ( - "https://docs.djangoproject.com/en/5.0/", - "http://docs.djangoproject.com/en/5.0/_objects/", + "https://docs.djangoproject.com/en/5.1/", + "http://docs.djangoproject.com/en/5.1/_objects/", ), "pymongo": ("https://pymongo.readthedocs.io/en/stable/", None), "python": ("https://docs.python.org/3/", None), diff --git a/docs/source/index.rst b/docs/source/index.rst index 4f80bed6..2e2adaf9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,4 +1,4 @@ -django-mongodb-backend 5.0.x documentation +django-mongodb-backend 5.1.x documentation ========================================== .. toctree:: diff --git a/pyproject.toml b/pyproject.toml index 67650474..b83e6114 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ keywords = [ classifiers = [ "Development Status :: 4 - Beta", "Framework :: Django", - "Framework :: Django :: 5.0", + "Framework :: Django :: 5.1", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", diff --git a/requirements.txt b/requirements.txt index 214677f9..04dc753d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -# NOTE: this needs to change per branch to track the django version. -django>=5.0,<5.1 +django>=5.1,<5.2 pymongo>=4.6,<5.0 diff --git a/tests/schema_/test_embedded_model.py b/tests/schema_/test_embedded_model.py index 7bb4e211..2ae6108a 100644 --- a/tests/schema_/test_embedded_model.py +++ b/tests/schema_/test_embedded_model.py @@ -1,9 +1,8 @@ import itertools from django.db import connection, models -from django.test import TransactionTestCase, ignore_warnings +from django.test import TransactionTestCase from django.test.utils import isolate_apps -from django.utils.deprecation import RemovedInDjango51Warning from django_mongodb_backend.fields import EmbeddedModelField from django_mongodb_backend.models import EmbeddedModel @@ -131,54 +130,6 @@ def test_unique(self): editor.delete_model(Book) self.assertTableNotExists(Author) - @ignore_warnings(category=RemovedInDjango51Warning) - @isolate_apps("schema_") - def test_index_together(self): - """Meta.index_together on an embedded model.""" - - class Address(EmbeddedModel): - index_together_one = models.CharField(max_length=10) - index_together_two = models.CharField(max_length=10) - - class Meta: - app_label = "schema_" - index_together = [("index_together_one", "index_together_two")] - - class Author(models.Model): - address = EmbeddedModelField(Address) - index_together_three = models.CharField(max_length=10) - index_together_four = models.CharField(max_length=10) - - class Meta: - app_label = "schema_" - index_together = [("index_together_three", "index_together_four")] - - class Book(models.Model): - author = EmbeddedModelField(Author) - - class Meta: - app_label = "schema_" - - with connection.schema_editor() as editor: - editor.create_model(Book) - self.assertTableExists(Book) - # Embedded uniques are created. - self.assertEqual( - self.get_constraints_for_columns( - Book, ["author.address.index_together_one", "author.address.index_together_two"] - ), - ["schema__add_index_t_efa93e_idx"], - ) - self.assertEqual( - self.get_constraints_for_columns( - Book, - ["author.index_together_three", "author.index_together_four"], - ), - ["schema__aut_index_t_df32aa_idx"], - ) - editor.delete_model(Book) - self.assertTableNotExists(Book) - @isolate_apps("schema_") def test_unique_together(self): """Meta.unique_together on an embedded model.""" @@ -376,70 +327,6 @@ class Meta: editor.delete_model(Book) self.assertTableNotExists(Author) - @ignore_warnings(category=RemovedInDjango51Warning) - @isolate_apps("schema_") - def test_add_remove_field_index_together(self): - """AddField/RemoveField + EmbeddedModelField + Meta.index_together.""" - - class Address(models.Model): - index_together_one = models.CharField(max_length=10) - index_together_two = models.CharField(max_length=10) - - class Meta: - app_label = "schema_" - index_together = [("index_together_one", "index_together_two")] - - class Author(models.Model): - address = EmbeddedModelField(Address) - index_together_three = models.CharField(max_length=10) - index_together_four = models.CharField(max_length=10) - - class Meta: - app_label = "schema_" - index_together = [("index_together_three", "index_together_four")] - - class Book(models.Model): - class Meta: - app_label = "schema_" - - new_field = EmbeddedModelField(Author) - new_field.set_attributes_from_name("author") - with connection.schema_editor() as editor: - # Create the table amd add the field. - editor.create_model(Book) - editor.add_field(Book, new_field) - # Embedded index_togethers are created. - self.assertEqual( - self.get_constraints_for_columns( - Book, ["author.address.index_together_one", "author.address.index_together_two"] - ), - ["schema__add_index_t_efa93e_idx"], - ) - self.assertEqual( - self.get_constraints_for_columns( - Book, - ["author.index_together_three", "author.index_together_four"], - ), - ["schema__aut_index_t_df32aa_idx"], - ) - editor.remove_field(Book, new_field) - # Embedded indexes are removed. - self.assertEqual( - self.get_constraints_for_columns( - Book, ["author.address.index_together_one", "author.address.index_together_two"] - ), - [], - ) - self.assertEqual( - self.get_constraints_for_columns( - Book, - ["author.index_together_three", "author.index_together_four"], - ), - [], - ) - editor.delete_model(Book) - self.assertTableNotExists(Book) - @isolate_apps("schema_") def test_add_remove_field_unique_together(self): """AddField/RemoveField + EmbeddedModelField + Meta.unique_together."""