Skip to content

feat(django-google-spanner): support Django 6.0 - #18128

Open
sakthivelmanii wants to merge 3 commits into
mainfrom
add-django-6.0-support
Open

feat(django-google-spanner): support Django 6.0#18128
sakthivelmanii wants to merge 3 commits into
mainfrom
add-django-6.0-support

Conversation

@sakthivelmanii

@sakthivelmanii sakthivelmanii commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds support for Django 6.0 in django-google-spanner while maintaining full backwards compatibility with Django 5.2.

Fixes #18053


Key Changes

  • Covering Indexes (STORING): Added support for include clauses in index creation, generating Cloud Spanner GoogleSQL STORING (col1, col2) DDL; declared supports_covering_indexes = True.
  • Composite Primary Keys: Declared support for Django 6.0 composite primary keys (supports_composite_primary_keys = True).
  • DML Returning Clauses: Enabled can_return_columns_from_insert = True and updated returning_columns() in operations.py to safely handle column names and expression objects, allowing Django to automatically populate database defaults and GeneratedField values on INSERT ... THEN RETURN.

Behavioral Notes for Users

  • DML THEN RETURN: With can_return_columns_from_insert = True, Django will now generate THEN RETURN clauses for models with database-generated defaults or GeneratedField columns upon .save(). Applications wishing to preserve legacy behavior can opt out via AppConfig:
    from django.apps import AppConfig
    
    class MyAppConfig(AppConfig):
        name = "myapp"
    
        def ready(self):
            from django_spanner.features import DatabaseFeatures
            DatabaseFeatures.can_return_columns_from_insert = False
    

Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Make sure to open an issue as a bug/issue before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

Fixes #18053 🦕

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for Django 6.0, including dependency updates, tuple-casting for lookup parameters, an asynchronous autocommit setter, and a Spanner-specific JSON path compiler. The review feedback highlights critical improvements: resolving syntax, type, and SQL injection issues in compile_json_path by utilizing json.dumps; wrapping the async autocommit operation in self.execute_wrapper to align with Django standards; and fixing invalid shell syntax and compatibility issues in the new test suite script.

Comment thread packages/django-google-spanner/django_spanner/operations.py Outdated
Comment thread packages/django-google-spanner/django_spanner/base.py Outdated
Comment thread packages/django-google-spanner/django_test_suite_6.0.sh Outdated
Comment thread packages/django-google-spanner/setup.py Outdated
@sakthivelmanii
sakthivelmanii force-pushed the add-django-6.0-support branch 4 times, most recently from 311d5d1 to cfa5943 Compare August 19, 2026 13:39
@sakthivelmanii
sakthivelmanii marked this pull request as ready for review August 19, 2026 13:39
@sakthivelmanii
sakthivelmanii requested review from a team as code owners August 19, 2026 13:40
Comment thread packages/django-google-spanner/setup.py
@parthea

parthea commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for Django 6.0 in the django-google-spanner package. Key changes include updating supported versions and dependencies, implementing async autocommit handling, adding Django 6.0 test exclusions, and updating database features, operations, and schema editors to support index inclusion columns and returning columns from inserts. Feedback on these changes highlights two important issues: first, the _index_include_sql helper in schema.py should resolve actual database column names via model._meta.get_field(field).column rather than using str(field) directly; second, the async autocommit wrapper in base.py should use thread_sensitive=True with sync_to_async to ensure thread safety and prevent connection sharing issues.

Comment thread packages/django-google-spanner/django_spanner/schema.py
Comment thread packages/django-google-spanner/django_spanner/base.py Outdated
Comment thread packages/django-google-spanner/django_test_suite_6.0.sh Outdated
@sakthivelmanii
sakthivelmanii force-pushed the add-django-6.0-support branch from cfa5943 to b219da1 Compare August 19, 2026 16:05
Comment thread packages/django-google-spanner/django_spanner/operations.py Outdated
Comment thread packages/django-google-spanner/django_spanner/lookups.py Outdated
"sqlparse >= 0.3.0",
"google-cloud-spanner >= 3.13.0",
"django >= 5.2, < 6.0",
"google-cloud-spanner >= 3.69.1",

@parthea parthea Aug 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please can you clarify if we really need to bump google-cloud-spanner? Do tests fail with 3.13.0?

Is there a lower minimum that we can set here?

If 3.69.1 is yanked or has a regression, users may not be able to install/use the latest version of django-google-spanner
https://pypi.org/project/google-cloud-spanner/3.69.1/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#18152 This fix is needed for can_return_columns_from_insert to work correctly. I will update this version to 3.69.2 once we have a new version released. I am planning to push this change to 3.70.0 or 3.69.3

}

can_introspect_duration_field = False
can_return_columns_from_insert = True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add this to the list to declare support for STORING:

supports_covering_indexes=True

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

CommitRequest,
CreateSessionRequest,
ExecuteSqlRequest,
TypeCode,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this test is actually used, at least not by default. The noxfile.py says this:

UNIT_TEST_DEPENDENCIES = [
    "django~=5.2",
    "sqlparse==0.3.1",
]

UNIT_TEST_MOCKSERVER_DEPENDENCIES = [
    "django~=5.2",
    "google-cloud-spanner>=3.55.0",
    "sqlparse>=0.4.4",
]

So I think that we should update noxfile.py as well to also run tests with Django 6.0

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

USE_EMULATOR = os.getenv("SPANNER_EMULATOR_HOST") is not None

SUPPORTED_DJANGO_VERSIONS = [(5, 2)]
SUPPORTED_DJANGO_VERSIONS = [(6, 0), (5, 2)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also update the README file to document support for 6.0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also do a full search of the project to look for other outdated references to Django versions? I think there are more than only those in the README in various docstrings and comments.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

can_introspect_duration_field = False
can_return_columns_from_insert = True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that setting this:

can_return_columns_from_insert = True

Can have an impact on existing applications. Django will now start generating THEN RETURN clauses for (some) insert statements. That means that:

  1. Those statements are not compatible with ExecuteBatchDml (meaning: execute_many in dbapi)
  2. If anyone somehow manages to use this new Django provider with an older Spanner dbapi driver version that does not contain the fix for THEN RETURN in auto-commit, could suddenly see errors.

Considering the fact that we require the newest version of the dbapi driver for this Django provider, I think that this should be acceptable, but we should document this in the release notes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really part of this change, but it came up during the review: We should make this user_agent string dynamic like this:

f"django_spanner/{__version__}"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"VALUES (@a0, @a1, @a2)",
1,
"VALUES (@a0, @a1, @a2) "
"THEN RETURN id",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am starting to worry / think / wonder whether this is desired behavior for existing customers. Anyone currently using this provider with Django 5.2 will (by default) not get any DML statements that include THEN RETURN clauses. All of a sudden changing that default is a bit worrisome, because:

  1. It does not support ExecuteBatchDml (but it is highly unlikely that anyone is using that with generated SQL statements from Django, so not really an argument)
  2. It does not work with earlier versions of the dbapi driver when using AutoCommit=True.
  3. It changes the behavior for all GeneratedFields and db_default fields. These are now automatically updated when save() is called. And while that in general is an improvement, it is also a behavior change for an existing application. If an application (implicitly) relied on a field with a default/generated value to remain unchanged after a save() operation, then that could fail now.

So I think that based on the above, the risk is low, but not zero, that it could affect existing applications. I think that we therefore should document this properly, and maybe also add an example for how an application can return to the previous behavior, for example like this:

from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'myapp'

    def ready(self):
        from django_spanner.features import DatabaseFeatures
        DatabaseFeatures.can_return_columns_from_insert = False

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(django-spanner): support Django 6.0

3 participants