@@ -8,11 +8,13 @@ This is a lightweight ORM written in Python and built on top of Cloud Spanner.
88
99Make sure that Python 3.7 is the default version of python for your environment,
1010then run:
11- ``` pip install spanner-orm `` `
11+ ` pip install spanner-orm `
1212
1313### Connecting
14+
1415To connect the Spanner ORM to an existing Spanner database:
15- ``` python
16+
17+ ``` python
1618import spanner_orm
1719spanner_orm.connect(instance_name, database_name)
1820```
@@ -27,9 +29,11 @@ libraries seems to not work, and the thread code associated with using the Pingi
2729also seems to not do what is intended (ping the pool every so often)
2830
2931### Creating a model
32+
3033In order to write to and read from a table on Spanner, you need to tell the ORM
3134about the table by writing a model class, which looks something like this:
32- ``` python
35+
36+ ``` python
3337import spanner_orm
3438
3539class TestModel (spanner_orm .Model ):
@@ -67,7 +71,7 @@ the corresponding table on the database through the ORM in one of two ways. If
6771the database has not yet been created, we can create it and the table at the
6872same time by:
6973
70- ``` python
74+ ``` python
7175admin_api = spanner_orm.connect_admin(
7276 ' instance_name' ,
7377 ' database_name' ,
@@ -79,16 +83,16 @@ If the database already exists, we can execute a Migration where the upgrade
7983method returns a CreateTable for the model you have just defined (see section
8084on migrations)
8185
82-
8386### Retrieve data from Spanner
87+
8488All queries through Spanner take place in a
8589[ transaction] ( https://cloud.google.com/spanner/docs/transactions ) . The ORM
8690usually expects a transaction to be present and provided, but if None is
8791specified, a new transaction will be created for that request.
88- The two main ways of retrieving data through the ORM are ``` where() `` ` and
89- ``` find() ``` / ``` find_multi() `` ` :
92+ The two main ways of retrieving data through the ORM are ` where() ` and
93+ ` find() ` / ` find_multi() ` :
9094
91- ``` python
95+ ``` python
9296# where() is invokes on a model class to retrieve models of that tyep. it takes a
9397# transaction and then a sequence of conditions.
9498# Most conditions that specify a Field, Index, Relationship, or Model can take
@@ -118,56 +122,63 @@ specific_object = finder(1)
118122```
119123
120124### Write data to Spanner
125+
121126The simplest way to write data is to create a Model (or retrieve one and modify
122127it) and then call save() on it:
123- ``` python
128+
129+ ``` python
124130test_model = TestModel({' key' : ' key' , ' value' : 1 })
125131test_model.save()
126132```
133+
127134Note that creating a model as per above will fail if there's already a row in
128135the database where the primary key matches, as it uses a Spanner INSERT instead
129136of an UPDATE, as the ORM thinks it's a new object, as it wasn't retrieved from
130137Spanner.
131138
132- For modifying multiple objects at the same time, the Model ``` save_batch() `` ` method
139+ For modifying multiple objects at the same time, the Model ` save_batch() ` method
133140can be used:
134- ``` python
141+
142+ ``` python
135143models = []
136144for i in range (10 ):
137145 key = ' test_{} ' .format(i)
138146 models.append(TestModel({' key' : key, ' value' : value}))
139147TestModel.save_batch(None , models)
140148```
141149
142- ``` spanner_orm.spanner_api().run_write() `` ` can be used for executing read-write
143- transactions, or the ``` transactional_write `` ` decorator can be used similarly
150+ ` spanner_orm.spanner_api().run_write() ` can be used for executing read-write
151+ transactions, or the ` transactional_write ` decorator can be used similarly
144152to the read decorator mentioned above. Note that if a transaction fails due to
145153data being modified after the read happened and before the transaction finished
146154executing, the called method will be re-run until it succeeds or a certain
147- number of failures happen. Make sure that there are no side effects that could
155+ number of failures happen. Make sure that there are no side effects that could
148156cause issues if called multiple times. Exceptions thrown out of the called
149157method will abort the transaction.
150158
151- Other helper methods exist for more complex use cases (``` create ``` , ``` update `` ` ,
152- ``` upsert `` ` , and others), but you will have to do more work in order to use those
159+ Other helper methods exist for more complex use cases (` create ` , ` update ` ,
160+ ` upsert ` , and others), but you will have to do more work in order to use those
153161correctly. See the documentation on those methods for more information.
154162
155163## Migrations
164+
156165### Creating migrations
157- Running ``` spanner-orm generate <migration name> ``` will generate a new
166+
167+ Running ` spanner-orm generate <migration name> ` will generate a new
158168migration file to be filled out in the directory specified (or 'migrations' by
159- default). The ``` upgrade `` ` function is executed when migrating, and the
160- ``` downgrade `` ` function is executed when rolling back the migration. Each of
169+ default). The ` upgrade ` function is executed when migrating, and the
170+ ` downgrade ` function is executed when rolling back the migration. Each of
161171these should return a single SchemaUpdate object (e.g., CreateTable, AddColumn,
162172etc.), as Spanner cannot execute multiple schema updates atomically.
163173
164174### Executing migrations
165- Running ``` spanner-orm migrate <Spanner instance> <Spanner database> ``` will
175+
176+ Running ` spanner-orm migrate <Spanner instance> <Spanner database> ` will
166177execute all the unmigrated migrations for that database in the correct order,
167178using the application default credentials. If that won't work for your use case,
168- ``` MigrationExecutor `` ` can be used instead:
179+ ` MigrationExecutor ` can be used instead:
169180
170- ``` python
181+ ``` python
171182connection = spanner_orm.SpannerConnection(
172183 instance_name,
173184 database_name,
@@ -176,12 +187,22 @@ executor = spanner_orm.MigrationExecutor(connection)
176187executor.migrate()
177188```
178189
190+ - ** Note:** If you need ` MigrationExecutor ` to import migration files under a
191+ particular package name (i.e. not as parent packages) then provide the package
192+ name and migration files will be imported using the full module name like so:
193+
194+ ``` python
195+ # Migration files will be imported as `project.migrations.migration_name`
196+ # Allows you to import other modules from `project` into your migration files
197+ executor = spanner_orm.MigrationExecutor(connection, pkg_name = " project.migrations" )
198+ ```
199+
179200Note that there is no protection against trying execute migrations concurrently
180201multiple times, so try not to do that.
181202
182203If a migration needs to be rolled back,
183- ``` spanner-orm rollback <migration_name> <Spanner instance> <Spanner database> `` `
184- or the corresponding ``` MigrationExecutor `` ` method should be used.
204+ ` spanner-orm rollback <migration_name> <Spanner instance> <Spanner database> `
205+ or the corresponding ` MigrationExecutor ` method should be used.
185206
186- To see a list of all migrations found, run ``` spanner-orm showmigrations <Spanner instance> <Spanner database> `` ` .
207+ To see a list of all migrations found, run ` spanner-orm showmigrations <Spanner instance> <Spanner database> ` .
187208Migrations that have already been applied migrations are marked by an ` [X] ` .
0 commit comments