Skip to content

Commit 3e47ec5

Browse files
feat(django-spanner): support covering indexes, dynamic user-agent, and docs for Django 6.0
1 parent febda9c commit 3e47ec5

10 files changed

Lines changed: 50 additions & 19 deletions

File tree

packages/django-google-spanner/README.rst

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,13 @@ Supported versions
6565
~~~~~~~~~~~~~~~~~~
6666

6767
The library supports `Django 5.2
68-
<https://docs.djangoproject.com/en/5.2/>`_.
68+
<https://docs.djangoproject.com/en/5.2/>`_ and `Django 6.0
69+
<https://docs.djangoproject.com/en/6.0/>`_.
6970
The minimum required Python version is 3.10.
7071

7172
.. code:: shell
7273
73-
pip3 install django==5.2
74+
pip3 install "django>=5.2,<6.1"
7475
7576
7677
Installing the package
@@ -88,7 +89,7 @@ To install from source:
8889
.. code:: shell
8990
9091
git clone git@github.com:googleapis/google-cloud-python.git
91-
cd python-spanner-django
92+
cd packages/django-google-spanner
9293
pip3 install -e .
9394
9495
@@ -266,6 +267,26 @@ By participating in this project you agree to abide by its terms. See the `Code
266267
of Conduct <https://github.com/googleapis/google-cloud-python/blob/main/CODE_OF_CONDUCT.md>`_ for more information.
267268

268269

270+
DML RETURNING Behavior
271+
~~~~~~~~~~~~~~~~~~~~~~
272+
273+
Starting with Django 6.0 compatibility, ``can_return_columns_from_insert = True`` is enabled. Django will generate ``THEN RETURN`` clauses for insert statements that create model instances with database-generated defaults or ``GeneratedField`` columns.
274+
275+
If your application relies on the previous behavior (where returned columns were not queried automatically upon insert), you can disable it in your Django ``AppConfig``:
276+
277+
.. code:: python
278+
279+
from django.apps import AppConfig
280+
281+
class MyAppConfig(AppConfig):
282+
name = "myapp"
283+
284+
def ready(self):
285+
from django_spanner.features import DatabaseFeatures
286+
287+
DatabaseFeatures.can_return_columns_from_insert = False
288+
289+
269290
Limitations
270291
~~~~~~~~~~~
271292

packages/django-google-spanner/django_spanner/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from .introspection import DatabaseIntrospection
1717
from .operations import DatabaseOperations
1818
from .schema import DatabaseSchemaEditor
19+
from .version import __version__
1920

2021
# Global cache for Spanner client to prevent multiple initializations
2122
# which can cause OpenTelemetry 'MeterProvider override' crashes.
@@ -160,7 +161,7 @@ def get_connection_params(self):
160161
"project": self._get_project_id(),
161162
"instance_id": self.settings_dict["INSTANCE"],
162163
"database_id": self.settings_dict["NAME"],
163-
"user_agent": "django_spanner/2.2.0a1",
164+
"user_agent": f"django_spanner/{__version__}",
164165
**self.settings_dict["OPTIONS"],
165166
}
166167

packages/django-google-spanner/django_spanner/features.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def introspected_field_types(self):
7171
# Spanner does not support order by null modifiers.
7272
supports_order_by_nulls_modifier = False
7373
supports_any_value = True
74+
supports_covering_indexes = True
7475
# Spanner does not support SELECTing an arbitrary expression that also
7576
# appears in the GROUP BY clause.
7677
supports_subqueries_in_group_by = False
@@ -214,6 +215,11 @@ def introspected_field_types(self):
214215
"many_to_one_null.tests.ManyToOneNullTests.test_set_clear_non_bulk",
215216
"many_to_one_null.tests.ManyToOneNullTests.test_unsaved",
216217
"foreign_object.tests.MultiColumnFKTests.test_prefetch_foreignobject_reverse",
218+
# Indexes tests: Spanner uses STORING instead of PostgreSQL's INCLUDE syntax
219+
# and does not support partial (WHERE) indexes. Upstream test assertions hardcode
220+
# the literal string 'INCLUDE', causing string assertion failures against Spanner's STORING clause.
221+
"indexes.tests.CoveringIndexTests.test_covering_index",
222+
"indexes.tests.CoveringIndexTests.test_covering_partial_index",
217223
# Admin ChangeList tests
218224
"admin_changelist.tests.ChangeListTests.test_custom_lookup_in_search_fields",
219225
"admin_changelist.tests.ChangeListTests.test_deterministic_order_for_model_ordered_by_its_manager",

packages/django-google-spanner/django_spanner/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def check_django_compatability(supported_django_versions):
1313
"""
1414
Verify that this version of django-spanner is compatible with the installed
1515
version of Django. For example, django-spanner is compatible
16-
with Django 2.2.y and 3.2.z
16+
with Django 5.2.x and 6.0.y
1717
"""
1818
from . import __version__
1919

packages/django-google-spanner/docs/samples.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ This `Example <example_from_scratch.html>`_ shows how to use django-spanner for
99
django-spanner on healthchecks.io
1010
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1111

12-
This `Example <example_healthchecks.html>`_ shows how to use django-spanner for Cloud Spanner as a backend database for `Django's tutorials <https://docs.djangoproject.com/en/2.2/intro/tutorial01/>`_
12+
This `Example <example_healthchecks.html>`_ shows how to use django-spanner for Cloud Spanner as a backend database for `Django's tutorials <https://docs.djangoproject.com/en/5.2/intro/tutorial01/>`_

packages/django-google-spanner/noxfile.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@
5656
]
5757

5858
UNIT_TEST_DEPENDENCIES = [
59-
"django~=5.2",
60-
"sqlparse==0.3.1",
59+
"django>=5.2,<6.1",
60+
"sqlparse>=0.3.1",
6161
]
6262

6363
UNIT_TEST_MOCKSERVER_DEPENDENCIES = [
64-
"django~=5.2",
65-
"google-cloud-spanner>=3.55.0",
64+
"django>=5.2,<6.1",
65+
"google-cloud-spanner>=3.69.1",
6666
"sqlparse>=0.4.4",
6767
]
6868

packages/django-google-spanner/run_testing_worker.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ def __exit__(self, exc, exc_value, traceback):
6767
print("creating instance with delay: {} seconds".format(delay))
6868
time.sleep(delay)
6969

70+
suite = os.environ.get("DJANGO_TEST_SUITE", "./django_test_suite_6.0.sh")
7071
with TestInstance() as instance_name:
7172
os.system(
72-
"""DJANGO_TEST_APPS="{apps}" SPANNER_TEST_INSTANCE={instance} bash ./django_test_suite_4.2.sh""".format(
73-
apps=" ".join(test_apps), instance=instance_name
73+
"""DJANGO_TEST_APPS="{apps}" SPANNER_TEST_INSTANCE={instance} bash {suite}""".format(
74+
apps=" ".join(test_apps), instance=instance_name, suite=suite
7475
)
7576
)

packages/django-google-spanner/tests/unit/django_spanner/simple_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from django_spanner.base import DatabaseWrapper
1010
from django_spanner.client import DatabaseClient
1111
from django_spanner.operations import DatabaseOperations
12+
from django_spanner.version import __version__
1213

1314
# from unittest import TestCase
1415
from tests._helpers import OpenTelemetryBase
@@ -22,7 +23,7 @@ def setUpClass(cls):
2223

2324
cls.INSTANCE_ID = "instance_id"
2425
cls.DATABASE_ID = "database_id"
25-
cls.USER_AGENT = "django_spanner/2.2.0a1"
26+
cls.USER_AGENT = f"django_spanner/{__version__}"
2627
cls.OPTIONS = {"option": "dummy"}
2728

2829
cls.settings_dict = {

packages/django-google-spanner/tests/unit/django_spanner/test_features.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def test_introspected_field_types(self):
2424
def test_spanner_specific_feature_flags(self):
2525
features = DatabaseFeatures(self.connection)
2626
self.assertTrue(features.supports_any_value)
27+
self.assertTrue(features.supports_covering_indexes)
2728
self.assertTrue(features.supports_stored_generated_columns)
2829
self.assertTrue(features.supports_composite_primary_keys)
2930
self.assertFalse(features.supports_subqueries_in_group_by)

packages/django-google-spanner/tests/unit/django_spanner/test_utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,23 @@
1515
class TestUtils(SpannerSimpleTestClass):
1616
SQL_WITH_WHERE = "Select 1 from Table WHERE 1=1"
1717
SQL_WITHOUT_WHERE = "Select 1 from Table"
18-
# Only active LTS django versions (2.2.*, 3.2.*) are supported by this library right now.
19-
SUPPORTED_DJANGO_VERSIONS = [(2, 2), (3, 2)]
18+
# Supported Django versions (5.2.*, 6.0.*) are supported by this library right now.
19+
SUPPORTED_DJANGO_VERSIONS = [(5, 2), (6, 0)]
2020

2121
def test_check_django_compatability_match(self):
2222
"""
2323
Checks django compatibility match.
2424
"""
25-
django_spanner.__version__ = "2.2"
26-
django.VERSION = (2, 2, 19, "alpha", 0)
25+
django_spanner.__version__ = "5.2"
26+
django.VERSION = (5, 2, 0, "final", 0)
2727
check_django_compatability(self.SUPPORTED_DJANGO_VERSIONS)
2828

2929
def test_check_django_compatability_mismatch(self):
3030
"""
3131
Checks django compatibility mismatch.
3232
"""
33-
django_spanner.__version__ = "2.2"
34-
django.VERSION = (3, 1, 19, "alpha", 0)
33+
django_spanner.__version__ = "5.2"
34+
django.VERSION = (4, 2, 0, "final", 0)
3535
with self.assertRaises(ImproperlyConfigured):
3636
check_django_compatability(self.SUPPORTED_DJANGO_VERSIONS)
3737

0 commit comments

Comments
 (0)