Conversation
Changing nullability of fields with indexes from Meta.indexes isn't supported. refs #378
…talled If setup.py imports django_spanner, Django and google.api_core.datetime_helpers must be installed before django-spanner. Regression in 3a244267ffc6cabe847511896a0616fa161d3bfe.
Detail how to run the Django test suite apps with the 2 different methods: * directly with django_test_suite.sh * by parallelization with ./bin/parallelize_linux
Audited and ran modeladmin and all tests passed with
```shell
test_actions_not_unique (modeladmin.test_checks.ActionsCheckTests) ... ok
test_actions_unique (modeladmin.test_checks.ActionsCheckTests) ... ok
test_custom_permissions_require_matching_has_method (modeladmin.test_checks.ActionsCheckTests) ... ok
test_autocomplete_e036 (modeladmin.test_checks.AutocompleteFieldsTests) ... ok
test_autocomplete_e037 (modeladmin.test_checks.AutocompleteFieldsTests) ... ok
test_autocomplete_e039 (modeladmin.test_checks.AutocompleteFieldsTests) ... ok
test_autocomplete_e040 (modeladmin.test_checks.AutocompleteFieldsTests) ... ok
test_autocomplete_e38 (modeladmin.test_checks.AutocompleteFieldsTests) ... ok
test_autocomplete_is_onetoone (modeladmin.test_checks.AutocompleteFieldsTests) ... ok
test_autocomplete_is_valid (modeladmin.test_checks.AutocompleteFieldsTests) ... ok
test_invalid_field_type (modeladmin.test_checks.DateHierarchyCheckTests) ... ok
test_missing_field (modeladmin.test_checks.DateHierarchyCheckTests) ... ok
test_related_invalid_field_type (modeladmin.test_checks.DateHierarchyCheckTests) ... ok
test_related_valid_case (modeladmin.test_checks.DateHierarchyCheckTests) ... ok
test_valid_case (modeladmin.test_checks.DateHierarchyCheckTests) ... ok
test_not_integer (modeladmin.test_checks.ExtraCheckTests) ... ok
test_valid_case (modeladmin.test_checks.ExtraCheckTests) ... ok
test_duplicate_fields_in_fields (modeladmin.test_checks.FieldsCheckTests) ... ok
test_inline (modeladmin.test_checks.FieldsCheckTests) ... ok
test_duplicate_fields (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_duplicate_fields_in_fieldsets (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_fieldsets_with_custom_form_validation (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_item_not_a_pair (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_missing_fields_key (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_non_iterable_item (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_not_iterable (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_second_element_of_item_not_a_dict (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_specified_both_fields_and_fieldsets (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_valid_case (modeladmin.test_checks.FieldsetsCheckTests) ... ok
test_invalid_field_type (modeladmin.test_checks.FilterHorizontalCheckTests) ... ok
test_missing_field (modeladmin.test_checks.FilterHorizontalCheckTests) ... ok
test_not_iterable (modeladmin.test_checks.FilterHorizontalCheckTests) ... ok
test_valid_case (modeladmin.test_checks.FilterHorizontalCheckTests) ... ok
test_invalid_field_type (modeladmin.test_checks.FilterVerticalCheckTests) ... ok
test_missing_field (modeladmin.test_checks.FilterVerticalCheckTests) ... ok
test_not_iterable (modeladmin.test_checks.FilterVerticalCheckTests) ... ok
test_valid_case (modeladmin.test_checks.FilterVerticalCheckTests) ... ok
test_missing_field (modeladmin.test_checks.FkNameCheckTests) ... ok
test_valid_case (modeladmin.test_checks.FkNameCheckTests) ... ok
test_fieldsets_with_custom_form_validation (modeladmin.test_checks.FormCheckTests) ... ok
test_invalid_type (modeladmin.test_checks.FormCheckTests) ... ok
test_valid_case (modeladmin.test_checks.FormCheckTests) ... ok
test_inline_without_formset_class (modeladmin.test_checks.FormsetCheckTests) ... ok
test_invalid_type (modeladmin.test_checks.FormsetCheckTests) ... ok
test_valid_case (modeladmin.test_checks.FormsetCheckTests) ... ok
test_invalid_callable (modeladmin.test_checks.InlinesCheckTests) ... ok
test_invalid_model (modeladmin.test_checks.InlinesCheckTests) ... ok
test_invalid_model_type (modeladmin.test_checks.InlinesCheckTests) ... ok
test_missing_model_field (modeladmin.test_checks.InlinesCheckTests) ... ok
test_not_correct_inline_field (modeladmin.test_checks.InlinesCheckTests) ... ok
test_not_iterable (modeladmin.test_checks.InlinesCheckTests) ... ok
test_not_model_admin (modeladmin.test_checks.InlinesCheckTests) ... ok
test_valid_case (modeladmin.test_checks.InlinesCheckTests) ... ok
test_both_list_editable_and_list_display_links (modeladmin.test_checks.ListDisplayEditableTests) ... ok
test_list_display_first_item_in_list_editable (modeladmin.test_checks.ListDisplayEditableTests) ... ok
test_list_display_first_item_in_list_editable_no_list_display_links (modeladmin.test_checks.ListDisplayEditableTests) ... ok
test_list_display_first_item_same_as_list_editable_first_item (modeladmin.test_checks.ListDisplayEditableTests) ... ok
test_list_display_first_item_same_as_list_editable_no_list_display_links (modeladmin.test_checks.ListDisplayEditableTests) ... ok
test_list_display_links_is_none (modeladmin.test_checks.ListDisplayEditableTests) ... ok
test_None_is_valid_case (modeladmin.test_checks.ListDisplayLinksCheckTests) ... ok
test_list_display_link_checked_for_list_tuple_if_get_list_display_overridden (modeladmin.test_checks.ListDisplayLinksCheckTests) ... ok
test_list_display_links_check_skipped_if_get_list_display_overridden (modeladmin.test_checks.ListDisplayLinksCheckTests) ... ok
test_missing_field (modeladmin.test_checks.ListDisplayLinksCheckTests) ... ok
test_missing_in_list_display (modeladmin.test_checks.ListDisplayLinksCheckTests) ... ok
test_not_iterable (modeladmin.test_checks.ListDisplayLinksCheckTests) ... ok
test_valid_case (modeladmin.test_checks.ListDisplayLinksCheckTests) ... ok
test_invalid_field_type (modeladmin.test_checks.ListDisplayTests) ... ok
test_missing_field (modeladmin.test_checks.ListDisplayTests) ... ok
test_not_iterable (modeladmin.test_checks.ListDisplayTests) ... ok
test_valid_case (modeladmin.test_checks.ListDisplayTests) ... ok
test_callable (modeladmin.test_checks.ListFilterTests) ... ok
test_list_filter_is_func (modeladmin.test_checks.ListFilterTests) ... ok
test_list_filter_validation (modeladmin.test_checks.ListFilterTests) ... ok
test_missing_field (modeladmin.test_checks.ListFilterTests) ... ok
test_not_associated_with_field_name (modeladmin.test_checks.ListFilterTests) ... ok
test_not_callable (modeladmin.test_checks.ListFilterTests) ... ok
test_not_filter (modeladmin.test_checks.ListFilterTests) ... ok
test_not_filter_again (modeladmin.test_checks.ListFilterTests) ... ok
test_not_filter_again_again (modeladmin.test_checks.ListFilterTests) ... ok
test_not_list_filter_class (modeladmin.test_checks.ListFilterTests) ... ok
test_valid_case (modeladmin.test_checks.ListFilterTests) ... ok
test_not_integer (modeladmin.test_checks.ListMaxShowAllCheckTests) ... ok
test_valid_case (modeladmin.test_checks.ListMaxShowAllCheckTests) ... ok
test_not_integer (modeladmin.test_checks.ListPerPageCheckTests) ... ok
test_valid_case (modeladmin.test_checks.ListPerPageCheckTests) ... ok
test_invalid_type (modeladmin.test_checks.ListSelectRelatedCheckTests) ... ok
test_valid_case (modeladmin.test_checks.ListSelectRelatedCheckTests) ... ok
test_not_integer (modeladmin.test_checks.MaxNumCheckTests) ... ok
test_valid_case (modeladmin.test_checks.MaxNumCheckTests) ... ok
test_not_integer (modeladmin.test_checks.MinNumCheckTests) ... ok
test_valid_case (modeladmin.test_checks.MinNumCheckTests) ... ok
test_invalid_expression (modeladmin.test_checks.OrderingCheckTests) ... ok
test_not_iterable (modeladmin.test_checks.OrderingCheckTests) ... ok
test_random_marker_not_alone (modeladmin.test_checks.OrderingCheckTests) ... ok
test_valid_case (modeladmin.test_checks.OrderingCheckTests) ... ok
test_valid_complex_case (modeladmin.test_checks.OrderingCheckTests) ... ok
test_valid_expression (modeladmin.test_checks.OrderingCheckTests) ... ok
test_valid_random_marker_case (modeladmin.test_checks.OrderingCheckTests) ... ok
test_invalid_field_type (modeladmin.test_checks.PrepopulatedFieldsCheckTests) ... ok
test_missing_field (modeladmin.test_checks.PrepopulatedFieldsCheckTests) ... ok
test_missing_field_again (modeladmin.test_checks.PrepopulatedFieldsCheckTests) ... ok
test_not_dictionary (modeladmin.test_checks.PrepopulatedFieldsCheckTests) ... ok
test_not_list_or_tuple (modeladmin.test_checks.PrepopulatedFieldsCheckTests) ... ok
test_one_to_one_field (modeladmin.test_checks.PrepopulatedFieldsCheckTests) ... ok
test_valid_case (modeladmin.test_checks.PrepopulatedFieldsCheckTests) ... ok
test_invalid_field_type (modeladmin.test_checks.RadioFieldsCheckTests) ... ok
test_invalid_value (modeladmin.test_checks.RadioFieldsCheckTests) ... ok
test_missing_field (modeladmin.test_checks.RadioFieldsCheckTests) ... ok
test_not_dictionary (modeladmin.test_checks.RadioFieldsCheckTests) ... ok
test_valid_case (modeladmin.test_checks.RadioFieldsCheckTests) ... ok
test_invalid_field_type (modeladmin.test_checks.RawIdCheckTests) ... ok
test_missing_field (modeladmin.test_checks.RawIdCheckTests) ... ok
test_not_iterable (modeladmin.test_checks.RawIdCheckTests) ... ok
test_valid_case (modeladmin.test_checks.RawIdCheckTests) ... ok
test_not_boolean (modeladmin.test_checks.SaveAsCheckTests) ... ok
test_valid_case (modeladmin.test_checks.SaveAsCheckTests) ... ok
test_not_boolean (modeladmin.test_checks.SaveOnTopCheckTests) ... ok
test_valid_case (modeladmin.test_checks.SaveOnTopCheckTests) ... ok
test_not_iterable (modeladmin.test_checks.SearchFieldsCheckTests) ... ok
test_inline_admin_inherited_valid (modeladmin.test_has_add_permission_obj_deprecation.HasAddPermissionObjTests) ... ok
test_inline_admin_valid (modeladmin.test_has_add_permission_obj_deprecation.HasAddPermissionObjTests) ... ok
test_inline_admin_warning (modeladmin.test_has_add_permission_obj_deprecation.HasAddPermissionObjTests) ... ok
test_model_admin_inherited_valid (modeladmin.test_has_add_permission_obj_deprecation.HasAddPermissionObjTests) ... ok
test_model_admin_valid (modeladmin.test_has_add_permission_obj_deprecation.HasAddPermissionObjTests) ... ok
test_has_add_permission (modeladmin.tests.ModelAdminPermissionTests) ... ok
test_has_change_permission (modeladmin.tests.ModelAdminPermissionTests) ... ok
test_has_delete_permission (modeladmin.tests.ModelAdminPermissionTests) ... ok
test_has_module_permission (modeladmin.tests.ModelAdminPermissionTests) ... ok
test_has_view_permission (modeladmin.tests.ModelAdminPermissionTests) ... ok
test_inline_has_add_permission_uses_obj (modeladmin.tests.ModelAdminPermissionTests) ... ok
test_inline_has_add_permission_without_obj (modeladmin.tests.ModelAdminPermissionTests) ... ok
----------------------------------------------------------------------
Ran 164 tests in 352.354s
OK
Destroying test database for alias 'default' ('test_oc42e8b2e0cd42c90f326')...
```
Despite us getting a quota bump, we continue to see errors related to creating instances, for example: https://source.cloud.google.com/results/invocations/7a40c996-e090-495d-945f-8d74f67d8473/targets/cloud-devrel%2Fclient-libraries%2Fpython%2Fgoogleapis%2Fpython-spanner-django%2Fpresubmit%2Fworker_0/log panic: rpc error: code = ResourceExhausted desc = Project 1065521786570 cannot add 1 nodes in region us-west2. This change lookups the zone and then extrapolates the region to use when creating the Cloud Spanner instance.
Regression in ff3f43d897c54e3a678811d4f076956975ec721a. fixes #427
Also while here, fixed a typo in a setting field for `DATABASES`
that used
DATABASE
instead of
NAME
for the name of the database.
Fixes #433
… INT64 columns' limitation refs #331
* chore(deps): update all dependencies * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
* chore: Changes for release please * Skipping timezone test
* chore(main): release 3.1.0 * Update CHANGELOG.md --------- Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> Co-authored-by: Ankit Agarwal <146331865+ankiaga@users.noreply.github.com>
* feat: Changes for Django 4.2 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Adding allow_transactions_in_auto_commit property * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Comments incorporated * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Update README.rst Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Update README.rst Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Update README.rst Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Update django_spanner/base.py Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Comments incorporated --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com> Co-authored-by: Knut Olav Løite <koloite@gmail.com>
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
* fix: Fixing README.rst to fix release failure. googleapis/python-spanner-django#883 * More changes * More changes
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
#914) Source-Link: googleapis/synthtool@de3def6 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:a1c5112b81d645f5bbc4d4bbc99d7dcb5089a52216c0e3fb1203a0eeabadd7d5 Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
…fig (#916) Source-Link: googleapis/synthtool@106d292 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:8ff1efe878e18bd82a0fb7b70bb86f77e7ab6901fed394440b6135db0ba8d84a Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
Source-Link: googleapis/synthtool@aa69fb7 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:f016446d6e520e5fb552c45b110cba3f217bffdd3d06bdddd076e9e6d13266cf Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
* test: add mockserver tests * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
* test: add mockserver tests * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix: add option to disable client-side pk generation Add an option to disable client-side generation of primary key values. This makes it possible to use the Spanner Django dialect in combination with other Django dialects, without the Spanner Django PK generation interfering with the other dialect(s). Fixes #783 * chore: introduce configuration option for id generation Adds a configuration option for enabling/disabling client-side ID generation using a random UUID. This allows users to better control ID generation when using non-Spanner databases in combination with Spanner. This option can in the future also be used for using IDENTITY columns for ID generation. --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
* fix: move version module to * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com> Co-authored-by: Knut Olav Løite <koloite@gmail.com>
Co-authored-by: Knut Olav Løite <koloite@gmail.com>
Document how to enable/disable randomly generated primary key values.
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Towards googleapis/librarian#2460 --------- Co-authored-by: ohmayr <omairn@google.com>
…/main' into migration.python-spanner-django.migration.2026-03-06_19-24-38.migrate
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request undertakes a significant structural reorganization by migrating the Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request migrates the python-spanner-django library into the monorepo. The changes are extensive as they include the entire codebase and configuration files. My review focused on identifying potential issues introduced during the migration, such as outdated configurations, inconsistencies, and minor bugs. I've found several issues, primarily in the CI/CD configuration files, where incorrect paths could lead to build failures. I've also noted some critical inconsistencies in documentation regarding Python version requirements, which have been highlighted with reference to repository guidelines on breaking changes. Additionally, there are a few minor code improvements. Overall, the migration looks good, but these issues should be addressed to ensure a smooth transition and maintainability.
I am having trouble creating individual review comments. Click here to see my feedback.
packages/django-google-spanner/.kokoro/samples/python3.10/periodic-head.cfg (9-10)
The TRAMPOLINE_BUILD_FILE path appears to be incorrect. It points to a file in the python-pubsub repository, which is likely a copy-paste error. It should probably point to a file within the python-spanner-django repository, such as .kokoro/test-samples-against-head.sh.
key: "TRAMPOLINE_BUILD_FILE"
value: "github/python-spanner-django/.kokoro/test-samples-against-head.sh"
packages/django-google-spanner/.kokoro/samples/python3.6/periodic-head.cfg (9-10)
The TRAMPOLINE_BUILD_FILE path appears to be incorrect. It points to a file in the python-pubsub repository, which is likely a copy-paste error. It should probably point to a file within the python-spanner-django repository, such as .kokoro/test-samples-against-head.sh.
key: "TRAMPOLINE_BUILD_FILE"
value: "github/python-spanner-django/.kokoro/test-samples-against-head.sh"
packages/django-google-spanner/.kokoro/samples/python3.7/periodic-head.cfg (9-10)
The TRAMPOLINE_BUILD_FILE path appears to be incorrect. It points to a file in the python-pubsub repository, which is likely a copy-paste error. It should probably point to a file within the python-spanner-django repository, such as .kokoro/test-samples-against-head.sh.
key: "TRAMPOLINE_BUILD_FILE"
value: "github/python-spanner-django/.kokoro/test-samples-against-head.sh"
packages/django-google-spanner/.kokoro/samples/python3.8/periodic-head.cfg (9-10)
The TRAMPOLINE_BUILD_FILE path appears to be incorrect. It points to a file in the python-pubsub repository, which is likely a copy-paste error. It should probably point to a file within the python-spanner-django repository, such as .kokoro/test-samples-against-head.sh.
key: "TRAMPOLINE_BUILD_FILE"
value: "github/python-spanner-django/.kokoro/test-samples-against-head.sh"
packages/django-google-spanner/.kokoro/samples/python3.9/periodic-head.cfg (9-10)
The TRAMPOLINE_BUILD_FILE path appears to be incorrect. It points to a file in the python-pubsub repository, which is likely a copy-paste error. It should probably point to a file within the python-spanner-django repository, such as .kokoro/test-samples-against-head.sh.
key: "TRAMPOLINE_BUILD_FILE"
value: "github/python-spanner-django/.kokoro/test-samples-against-head.sh"
packages/django-google-spanner/README.rst (72)
The README states that the minimum required Python version is 3.6. However, setup.py specifies python_requires=">=3.8". This inconsistency is critical. Given the setup.py configuration, the README should be updated to state Python 3.8 as the minimum version. Note that changing the minimum supported Python version is a breaking change in environment requirements, which typically warrants a minor version bump to allow for patching previous versions, as per repository guidelines.
The minimum required Python version is 3.8.
References
- Updating the minimum supported Python version from 3.6 to 3.8 constitutes a breaking change in environment requirements. Repository guidelines suggest a minor version bump for such changes to ensure proper versioning and support for users on older environments.
packages/django-google-spanner/setup.py (74)
The python_requires is set to >=3.8, which contradicts the README that states Python 3.6 is the minimum supported version. This inconsistency is critical. Please ensure all documentation and configuration files are consistent regarding the minimum Python version. If the minimum Python version is indeed 3.8, this represents a breaking change in environment requirements, which typically warrants a minor version bump to allow for patching previous versions, as per repository guidelines.
References
- Updating the minimum supported Python version from 3.6 to 3.8 constitutes a breaking change in environment requirements. Repository guidelines suggest a minor version bump for such changes to ensure proper versioning and support for users on older environments.
packages/django-google-spanner/.github/workflows/integration-tests-against-emulator-3.10.yml (21)
There's a typo in the step name. It should be "Set up Python 3.10" to match the Python version being set up in this workflow.
- name: Set up Python 3.10packages/django-google-spanner/CHANGELOG.md (7)
The release date (2025-06-05) is in the future. This is likely a typo and should be corrected to the actual release date.
packages/django-google-spanner/django_spanner/init.py (70-75)
The comments on lines 72 and 74 refer to a DISABLE_RANDOM_ID_GENERATION setting, but the code uses RANDOM_ID_GENERATION_ENABLED. To avoid confusion, the comments should be updated to use the correct setting name.
packages/django-google-spanner/django_spanner/base.py (155)
The user_agent is hardcoded to an old version string "django_spanner/2.2.0a1". This should be dynamically set using the __version__ from django_spanner.version to ensure it's always up-to-date. You'll need to import __version__ from .version at the top of the file.
"user_agent": f"django_spanner/{__version__}",
See #10953.
This PR should be merged with a merge-commit, not a squash-commit, in order to preserve the git history.