Skip to content

Commit eeb3d61

Browse files
committed
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.
1 parent 0c45d34 commit eeb3d61

File tree

4 files changed

+50
-7
lines changed

4 files changed

+50
-7
lines changed

django_spanner/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
# do that.
1313
from uuid import uuid4
1414

15+
RANDOM_ID_GENERATION_ENABLED_SETTING = "RANDOM_ID_GENERATION_ENABLED"
16+
1517
import pkg_resources
1618
from django.conf.global_settings import DATABASES
19+
from django.db import DEFAULT_DB_ALIAS
1720
from google.cloud.spanner_v1 import JsonObject
1821
from django.db.models.fields import (
1922
NOT_PROVIDED,
@@ -65,12 +68,22 @@ def autofield_init(self, *args, **kwargs):
6568
kwargs["blank"] = True
6669
Field.__init__(self, *args, **kwargs)
6770

71+
# The following behavior is chosen to prevent breaking changes with the original behavior.
72+
# 1. We use a client-side randomly generated int64 value for autofields if Spanner is the
73+
# default database, and DISABLE_RANDOM_ID_GENERATION has not been set.
74+
# 2. If Spanner is one of the non-default databases, and no value at all has been set for
75+
# DISABLE_RANDOM_ID_GENERATION, then we do not enable it. If there is a value for this
76+
# configuration option, then we use that value.
6877
databases = django.db.connections.databases
6978
for db, config in databases.items():
79+
default_enabled = str(db == DEFAULT_DB_ALIAS)
7080
if (
7181
config["ENGINE"] == "django_spanner"
7282
and self.default == NOT_PROVIDED
73-
and not config.get("DISABLE_RANDOM_ID_GENERATION", "false").lower() == "true"
83+
and config.get(
84+
RANDOM_ID_GENERATION_ENABLED_SETTING, default_enabled
85+
).lower()
86+
== "true"
7487
):
7588
self.default = gen_rand_int64
7689
break

tests/mockserver_tests/mock_server_test_base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,9 @@ def teardown_class(cls):
181181
def setup_method(self, test_method):
182182
for db, config in DATABASES.items():
183183
if config["ENGINE"] == "django_spanner":
184-
connections[db].settings_dict["OPTIONS"]["client"] = self.client
184+
connections[db].settings_dict["OPTIONS"][
185+
"client"
186+
] = self.client
185187
connections[db].settings_dict["OPTIONS"]["pool"] = self.pool
186188

187189
def teardown_method(self, test_method):

tests/mockserver_tests/test_basics.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
# limitations under the License.
1414
from google.cloud.spanner_v1 import (
1515
BatchCreateSessionsRequest,
16-
ExecuteSqlRequest, CommitRequest,
16+
ExecuteSqlRequest,
17+
CommitRequest,
1718
)
1819
from tests.mockserver_tests.mock_server_test_base import (
1920
MockServerTestBase,
2021
add_select1_result,
21-
add_singer_query_result, add_update_count,
22+
add_singer_query_result,
23+
add_update_count,
2224
)
2325
from django.db import connection, models
2426

@@ -77,7 +79,8 @@ def test_insert_singer(self):
7779
"INSERT INTO tests_singer "
7880
"(id, first_name, last_name) "
7981
"VALUES (@a0, @a1, @a2)",
80-
1)
82+
1,
83+
)
8184
singer = Singer(first_name="test", last_name="test")
8285
singer.save()
8386
requests = self.spanner_service.requests
@@ -107,7 +110,8 @@ class LocalSinger(models.Model):
107110
"INSERT INTO tests_localsinger "
108111
"(first_name, last_name) "
109112
"VALUES (@a0, @a1)",
110-
1)
113+
1,
114+
)
111115
singer = LocalSinger(first_name="test", last_name="test")
112116
singer.save()
113117
requests = self.spanner_service.requests

tests/unit/django_spanner/test_schema.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77

88
from .models import Author
9-
from django.db import NotSupportedError, connection
9+
from django.db import NotSupportedError, connection, connections
1010
from django.db.models import Index
1111
from django.db.models.fields import AutoField, IntegerField
1212
from django_spanner import gen_rand_int64
@@ -433,3 +433,27 @@ def test_autofield_not_spanner_w_default(self):
433433
assert gen_rand_int64 != field.default
434434
assert mock_func == field.default
435435
connection.settings_dict["ENGINE"] = "django_spanner"
436+
437+
def test_autofield_spanner_as_non_default_db_random_generation_enabled(
438+
self,
439+
):
440+
"""Not Spanner as the default db, default for field not provided."""
441+
connections.settings["default"]["ENGINE"] = "another_db"
442+
connections.settings["secondary"]["ENGINE"] = "django_spanner"
443+
connections.settings["secondary"][
444+
"RANDOM_ID_GENERATION_ENABLED"
445+
] = "true"
446+
field = AutoField(name="field_name")
447+
assert gen_rand_int64 == field.default
448+
connections.settings["default"]["ENGINE"] = "django_spanner"
449+
connections.settings["secondary"]["ENGINE"] = "django_spanner"
450+
del connections.settings["secondary"]["RANDOM_ID_GENERATION_ENABLED"]
451+
452+
def test_autofield_random_generation_disabled(self):
453+
"""Spanner, default is not provided."""
454+
connections.settings["default"][
455+
"RANDOM_ID_GENERATION_ENABLED"
456+
] = "false"
457+
field = AutoField(name="field_name")
458+
assert gen_rand_int64 != field.default
459+
del connections.settings["default"]["RANDOM_ID_GENERATION_ENABLED"]

0 commit comments

Comments
 (0)