Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Adding documentation for GA #665

Merged
merged 12 commits into from Jun 28, 2021
144 changes: 133 additions & 11 deletions README.rst
@@ -1,6 +1,37 @@
Cloud Spanner support for Django
================================

`Cloud Spanner`_ is the world's first fully managed relational database service
to offer both strong consistency and horizontal scalability for
mission-critical online transaction processing (OLTP) applications. With Cloud
Spanner you enjoy all the traditional benefits of a relational database; but
unlike any other relational database service, Cloud Spanner scales horizontally
to hundreds or thousands of servers to handle the biggest transactional
workloads.


- `Client Library Documentation`_
- `Product Documentation`_

.. _Cloud Spanner: https://cloud.google.com/spanner/
.. _Client Library Documentation: https://googleapis.dev/python/django-google-spanner/2.2.1b1/index.html
asthamohta marked this conversation as resolved.
Show resolved Hide resolved
.. _Product Documentation: https://cloud.google.com/spanner/docs

Quick Start
-----------

In order to use this library, you first need to go through the following steps:

1. `Select or create a Cloud Platform project.`_
2. `Enable billing for your project.`_
3. `Enable the Google Cloud Spanner API.`_
4. `Setup Authentication.`_

.. _Select or create a Cloud Platform project.: https://console.cloud.google.com/project
.. _Enable billing for your project.: https://cloud.google.com/billing/docs/how-to/modify-project#enable_billing_for_a_project
.. _Enable the Google Cloud Spanner API.: https://cloud.google.com/spanner
.. _Setup Authentication.: https://googleapis.dev/python/google-api-core/latest/auth.html

This package provides a `3rd-party database backend
<https://docs.djangoproject.com/en/2.2/ref/databases/#using-a-3rd-party-database-backend>`__
for using `Cloud Spanner <https://cloud.google.com/spanner>`__ with the `Django
Expand All @@ -11,10 +42,38 @@ under the hood.
Installation
------------

To use this library, you'll need a Google Cloud Platform project with the Cloud
Spanner API enabled. For details on enabling the API and authenticating with
GCP, see the `Cloud Spanner Python client library quickstart guide
<https://github.com/googleapis/python-spanner/#quick-start>`__.
Install this library in a `virtualenv`_ using pip. `virtualenv`_ is a tool to
create isolated Python and Django environments. The basic problem it addresses is one of
dependencies and versions, and indirectly permissions.

With `virtualenv`_, it's possible to install this library without needing system
install permissions, and without clashing with the installed system
dependencies.

.. _`virtualenv`: https://virtualenv.pypa.io/en/latest/


Mac/Linux
~~~~~~~~~

.. code-block:: console

pip install virtualenv
virtualenv <your-env>
source <your-env>/bin/activate
<your-env>/bin/pip install google-cloud-spanner
asthamohta marked this conversation as resolved.
Show resolved Hide resolved


Windows
~~~~~~~

.. code-block:: console

pip install virtualenv
virtualenv <your-env>
<your-env>\Scripts\activate
<your-env>\Scripts\pip.exe install google-cloud-spanner


Supported versions
~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -93,21 +152,84 @@ configured:
}


Set credentials and project environment variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You'll need to download a service account JSON key file and point to it using an environment variable:

.. code:: shell

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/keyfile.json
export GOOGLE_CLOUD_PROJECT=gcloud_project


Apply the migrations
~~~~~~~~~~~~~~~~~~~~

Please run:

.. code:: shell

$ python3 manage.py migrate


and that'll take a while running, but when done, it will look like the following
asthamohta marked this conversation as resolved.
Show resolved Hide resolved


After this you should can see the tables and indices created in your Cloud Spanner console
asthamohta marked this conversation as resolved.
Show resolved Hide resolved

Now run your server
~~~~~~~~~~~~~~~~~~~
After those migrations are completed, that will be all. Please continue on with the guides.

Create an Django admin user
~~~~~~~~~~~~~~~~~~~~~~~~~~~
First you’ll need to create a user who can login to the admin site. Run the following command:

.. code:: shell

$ python3 manage.py createsuperuser

which will then produce a prompt which will allow you to create your super user

.. code:: shell

Username: admin
Email address: admin@example.com
Password: **********
Password (again): **********
Superuser created successfully.


Login as admin
~~~~~~~~~~~~~~
Let’s run the server

.. code:: shell

python3 manage.py runserver

Then visit http://127.0.0.1:8000/admin/

Create and register your first model
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Please follow the guides in https://docs.djangoproject.com/en/2.2/intro/tutorial02/#creating-models
to create and register the model to the Django’s automatically-generated admin site.

How it works
------------

Overall design
~~~~~~~~~~~~~~

.. figure:: ./assets/overview.png
:alt:
.. figure:: https://raw.githubusercontent.com/googleapis/python-spanner-django/master/assets/internals.png
asthamohta marked this conversation as resolved.
Show resolved Hide resolved
:alt: "Overall Design"

Internals
~~~~~~~~~

.. figure:: ./assets/internals.png
:alt:

.. figure:: https://raw.githubusercontent.com/googleapis/python-spanner-django/master/assets/overview.png
:alt: "Internals"


Executing a query
Expand All @@ -134,11 +256,11 @@ Contributing

Contributions to this library are always welcome and highly encouraged.

See [CONTRIBUTING][contributing] for more information on how to get started.
See `CONTRIBUTING <https://github.com/googleapis/python-spanner-django/blob/master/CONTRIBUTING.md>`_ for more information on how to get started.

Please note that this project is released with a Contributor Code of Conduct.
By participating in this project you agree to abide by its terms. See the `Code
of Conduct <code-of-conduct.md>`_ for more information.
of Conduct <https://github.com/googleapis/python-spanner-django/blob/master/CODE_OF_CONDUCT.md>`_ for more information.

Current limitations
-------------------
Expand Down
4 changes: 3 additions & 1 deletion django_spanner/creation.py
Expand Up @@ -8,7 +8,6 @@
import sys
from unittest import skip

from django.conf import settings
from django.db.backends.base.creation import BaseDatabaseCreation
from django.utils.module_loading import import_string

Expand All @@ -21,6 +20,9 @@ class DatabaseCreation(BaseDatabaseCreation):

def mark_skips(self):
"""Skip tests that don't work on Spanner."""

asthamohta marked this conversation as resolved.
Show resolved Hide resolved
from django.conf import settings

for test_name in self.connection.features.skip_tests:
test_case_name, _, method_name = test_name.rpartition(".")
test_app = test_name.split(".")[0]
Expand Down
26 changes: 13 additions & 13 deletions django_spanner/functions.py
Expand Up @@ -38,7 +38,7 @@ def cast(self, compiler, connection, **extra_context):
A method to extend Django Cast class. Cast SQL query for given
parameters.

:type self: :class:`~django.db.models.functions.comparison.Cast`
:type self: :class:`~django.db.models.functions.Cast`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand Down Expand Up @@ -71,7 +71,7 @@ def chr_(self, compiler, connection, **extra_context):
A method to extend Django Chr class. Returns a SQL query where the code
points are displayed as a string.

:type self: :class:`~django.db.models.functions.text.Chr`
:type self: :class:`~django.db.models.functions.Chr`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand Down Expand Up @@ -101,7 +101,7 @@ def concatpair(self, compiler, connection, **extra_context):
A method to extend Django ConcatPair class. Concatenates a SQL query
into the sequence of :class:`IfNull` objects.

:type self: :class:`~django.db.models.functions.text.ConcatPair`
:type self: :class:`~django.db.models.functions.ConcatPair`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand Down Expand Up @@ -132,7 +132,7 @@ def cot(self, compiler, connection, **extra_context):
A method to extend Django Cot class. Returns a SQL query of calculated
trigonometric cotangent function.

:type self: :class:`~django.db.models.functions.math.Cot`
:type self: :class:`~django.db.models.functions.Cot`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand Down Expand Up @@ -162,7 +162,7 @@ def degrees(self, compiler, connection, **extra_context):
A method to extend Django Degress class. Returns a SQL query of the
angle converted to degrees.

:type self: :class:`~django.db.models.functions.math.Degrees`
:type self: :class:`~django.db.models.functions.Degrees`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand Down Expand Up @@ -190,8 +190,8 @@ def degrees(self, compiler, connection, **extra_context):
def left_and_right(self, compiler, connection, **extra_context):
"""A method to extend Django Left and Right classes.

:type self: :class:`~django.db.models.functions.text.Left` or
:class:`~django.db.models.functions.text.Right`
:type self: :class:`~django.db.models.functions.Left` or
:class:`~django.db.models.functions.Right`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand All @@ -216,7 +216,7 @@ def log(self, compiler, connection, **extra_context):
A method to extend Django Log class. Returns a SQL query of calculated
logarithm.

:type self: :class:`~django.db.models.functions.math.Log`
:type self: :class:`~django.db.models.functions.Log`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand Down Expand Up @@ -245,7 +245,7 @@ def ord_(self, compiler, connection, **extra_context):
A method to extend Django Ord class. Returns a SQL query of the
expression converted to ord.

:type self: :class:`~django.db.models.functions.text.Ord`
:type self: :class:`~django.db.models.functions.Ord`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand Down Expand Up @@ -275,7 +275,7 @@ def pi(self, compiler, connection, **extra_context):
A method to extend Django Pi class. Returns a SQL query of the Pi
constant.

:type self: :class:`~django.db.models.functions.math.Pi`
:type self: :class:`~django.db.models.functions.Pi`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand All @@ -302,7 +302,7 @@ def radians(self, compiler, connection, **extra_context):
A method to extend Django Radians class. Returns a SQL query of the
angle converted to radians.

:type self: :class:`~django.db.models.functions.math.Radians`
:type self: :class:`~django.db.models.functions.Radians`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand Down Expand Up @@ -332,7 +332,7 @@ def strindex(self, compiler, connection, **extra_context):
A method to extend Django StrIndex class. Returns a SQL query of the
string position.

:type self: :class:`~django.db.models.functions.text.StrIndex`
:type self: :class:`~django.db.models.functions.StrIndex`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand All @@ -359,7 +359,7 @@ def substr(self, compiler, connection, **extra_context):
A method to extend Django Substr class. Returns a SQL query of a
substring.

:type self: :class:`~django.db.models.functions.text.Substr`
:type self: :class:`~django.db.models.functions.Substr`
:param self: the instance of the class that owns this method.

:type compiler: :class:`~django_spanner.compiler.SQLCompilerst`
Expand Down
15 changes: 14 additions & 1 deletion django_spanner/operations.py
Expand Up @@ -10,7 +10,6 @@
from datetime import datetime, time
from uuid import UUID

from django.conf import settings
from django.db.backends.base.operations import BaseDatabaseOperations
from django.db.utils import DatabaseError
from django.utils import timezone
Expand Down Expand Up @@ -169,13 +168,17 @@ def adapt_datetimefield_value(self, value):
:rtype: :class:`~google.cloud.spanner_dbapi.types.TimestampStr`
:returns: Formatted Time.
"""
from django.conf import settings

if value is None:
return None
# Expression values are adapted by the database.
if hasattr(value, "resolve_expression"):
return value
# Cloud Spanner doesn't support tz-aware datetimes
if timezone.is_aware(value):
from django.conf import settings

if settings.USE_TZ:
value = timezone.make_naive(value, self.connection.timezone)
else:
Expand Down Expand Up @@ -301,6 +304,8 @@ def convert_datetimefield_value(self, value, expression, connection):
value.second,
value.microsecond,
)
from django.conf import settings
asthamohta marked this conversation as resolved.
Show resolved Hide resolved

return (
timezone.make_aware(dt, self.connection.timezone)
if settings.USE_TZ
Expand Down Expand Up @@ -377,6 +382,8 @@ def datetime_extract_sql(self, lookup_type, field_name, tzname):
:rtype: str
:returns: A SQL statement for extracting.
"""
from django.conf import settings

tzname = tzname if settings.USE_TZ else "UTC"
lookup_type = self.extract_names.get(lookup_type, lookup_type)
return 'EXTRACT(%s FROM %s AT TIME ZONE "%s")' % (
Expand Down Expand Up @@ -441,6 +448,8 @@ def datetime_trunc_sql(self, lookup_type, field_name, tzname):
:rtype: str
:returns: A SQL statement for truncating.
"""
from django.conf import settings

# https://cloud.google.com/spanner/docs/functions-and-operators#timestamp_trunc
tzname = tzname if settings.USE_TZ else "UTC"
if lookup_type == "week":
Expand Down Expand Up @@ -486,6 +495,8 @@ def datetime_cast_date_sql(self, field_name, tzname):
:rtype: str
:returns: A SQL statement for casting.
"""
from django.conf import settings

# https://cloud.google.com/spanner/docs/functions-and-operators#date
tzname = tzname if settings.USE_TZ else "UTC"
return 'DATE(%s, "%s")' % (field_name, tzname)
Expand All @@ -503,6 +514,8 @@ def datetime_cast_time_sql(self, field_name, tzname):
:rtype: str
:returns: A SQL statement for casting.
"""
from django.conf import settings

tzname = tzname if settings.USE_TZ else "UTC"
# Cloud Spanner doesn't have a function for converting
# TIMESTAMP to another time zone.
Expand Down
9 changes: 9 additions & 0 deletions docs/api-reference.rst
Expand Up @@ -7,3 +7,12 @@ The following classes and methods constitute the Django Spanner API.
:maxdepth: 1

schema-api
base-api
compiler-api
expressions-api
functions-api
introspection-api
lookups-api
utils-api
creation-api
operations-api