diff --git a/README.rst b/README.rst index 8f1ef0440d..e79e23cd91 100644 --- a/README.rst +++ b/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/latest/index.html +.. _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 `__ for using `Cloud Spanner `__ with the `Django @@ -11,10 +42,40 @@ 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 -`__. +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 + source /bin/activate + /bin/pip install python-spanner-django + /bin/pip install google-cloud-spanner + + +Windows +~~~~~~~ + +.. code-block:: console + + pip install virtualenv + virtualenv + \Scripts\activate + \Scripts\pip.exe install python-spanner-django + \Scripts\pip.exe install google-cloud-spanner + Supported versions ~~~~~~~~~~~~~~~~~~ @@ -93,52 +154,104 @@ 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 to run. After this you should be able to see the tables and indices created in your Cloud Spanner console. + +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/overview.png + :alt: "Overall Design" Internals ~~~~~~~~~ -.. figure:: ./assets/internals.png - :alt: - +.. figure:: https://raw.githubusercontent.com/googleapis/python-spanner-django/master/assets/internals.png + :alt: "Internals" Executing a query ~~~~~~~~~~~~~~~~~ -.. code:: python - - from google.cloud.spanner_dbapi import connect +Here is an example of how to add a row for Model Author, save it and later query it using Django - connection = connect('', '') - cursor = connection.cursor() - - cursor.execute( - "SELECT *" - "FROM Singers" - "WHERE SingerId = 15" - ) +.. code:: shell - results = cursor.fetchall() + >>> author_kent = Author( first_name="Arthur", last_name="Kent", rating=Decimal("4.1"),) + >>> author_kent.save() + >>> qs1 = Author.objects.all().values("first_name", "last_name") -Contributing ------------- +HOW TO CONTRIBUTE +----------------- Contributions to this library are always welcome and highly encouraged. -See [CONTRIBUTING][contributing] for more information on how to get started. +See `CONTRIBUTING `_ 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 `_ for more information. +By participating in this project you agree to abide by its terms. See the `Code +of Conduct `_ for more information. Current limitations ------------------- diff --git a/django_spanner/functions.py b/django_spanner/functions.py index 3cf3ec73b9..876e1f5a50 100644 --- a/django_spanner/functions.py +++ b/django_spanner/functions.py @@ -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` @@ -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` @@ -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` @@ -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` @@ -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` @@ -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` @@ -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` @@ -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` @@ -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` @@ -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` @@ -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` @@ -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` diff --git a/docs/api-reference.rst b/docs/api-reference.rst index c201e01e10..c17c4be455 100644 --- a/docs/api-reference.rst +++ b/docs/api-reference.rst @@ -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 diff --git a/docs/base-api.rst b/docs/base-api.rst new file mode 100644 index 0000000000..c27ed3acde --- /dev/null +++ b/docs/base-api.rst @@ -0,0 +1,7 @@ +Base API +===================== + +.. automodule:: django_spanner.base + :members: + :inherited-members: + \ No newline at end of file diff --git a/docs/compiler-api.rst b/docs/compiler-api.rst new file mode 100644 index 0000000000..8b598a5331 --- /dev/null +++ b/docs/compiler-api.rst @@ -0,0 +1,7 @@ +Compiler API +===================== + +.. automodule:: django_spanner.compiler + :members: + :inherited-members: + \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 301aa06e84..a176401457 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,11 @@ import sys import os import shlex +import django +sys.path.insert(0, os.path.abspath("..")) +os.environ["DJANGO_SETTINGS_MODULE"] = "tests.settings" +django.setup() # If extensions (or modules to document with autodoc) are in another directory, # add this directory to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. diff --git a/docs/creation-api.rst b/docs/creation-api.rst new file mode 100644 index 0000000000..fc194a18b3 --- /dev/null +++ b/docs/creation-api.rst @@ -0,0 +1,7 @@ +Creation API +===================== + +.. automodule:: django_spanner.creation + :members: + :inherited-members: + \ No newline at end of file diff --git a/docs/expressions-api.rst b/docs/expressions-api.rst new file mode 100644 index 0000000000..1a33643d74 --- /dev/null +++ b/docs/expressions-api.rst @@ -0,0 +1,7 @@ +Expressions API +===================== + +.. automodule:: django_spanner.expressions + :members: + :inherited-members: + \ No newline at end of file diff --git a/docs/functions-api.rst b/docs/functions-api.rst new file mode 100644 index 0000000000..50015e9f19 --- /dev/null +++ b/docs/functions-api.rst @@ -0,0 +1,7 @@ +Functions API +===================== + +.. automodule:: django_spanner.functions + :members: + :inherited-members: + \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index ca432559cf..ec9b23a055 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,7 +6,7 @@ Usage Documentation :maxdepth: 1 :titlesonly: - schema-usage + samples API Documentation ----------------- diff --git a/docs/introspection-api.rst b/docs/introspection-api.rst new file mode 100644 index 0000000000..38fee1d014 --- /dev/null +++ b/docs/introspection-api.rst @@ -0,0 +1,7 @@ +Introspection API +===================== + +.. automodule:: django_spanner.introspection + :members: + :inherited-members: + \ No newline at end of file diff --git a/docs/lookups-api.rst b/docs/lookups-api.rst new file mode 100644 index 0000000000..51a1a208cc --- /dev/null +++ b/docs/lookups-api.rst @@ -0,0 +1,7 @@ +Lookups API +===================== + +.. automodule:: django_spanner.lookups + :members: + :inherited-members: + \ No newline at end of file diff --git a/docs/operations-api.rst b/docs/operations-api.rst new file mode 100644 index 0000000000..a5b8e52a61 --- /dev/null +++ b/docs/operations-api.rst @@ -0,0 +1,7 @@ +Operations API +===================== + +.. automodule:: django_spanner.operations + :members: + :inherited-members: + \ No newline at end of file diff --git a/docs/samples.rst b/docs/samples.rst new file mode 100644 index 0000000000..4d9ef417a2 --- /dev/null +++ b/docs/samples.rst @@ -0,0 +1,24 @@ +Sample Code +#################################### + +Create and register your first model +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To define your database layout create a models file in your app folder and add the relevant +classes to it. Spanner works exactly like any other database you may have used with Django. +Here is a simple example you can run with Spanner. In our poll application below we create +the following two models: + +.. code:: python + + from django.db import models + + class Question(models.Model): + question_text = models.CharField(max_length=200) + pub_date = models.DateTimeField('date published') + def __str__(self): + return str(self.rating) + + class Choice(models.Model): + question = models.ForeignKey(Question, on_delete=models.CASCADE) + choice_text = models.CharField(max_length=200) + votes = models.IntegerField(default=0) diff --git a/docs/schema-api.rst b/docs/schema-api.rst index c0118ed24a..00c4954e99 100644 --- a/docs/schema-api.rst +++ b/docs/schema-api.rst @@ -4,5 +4,4 @@ Schema API .. automodule:: django_spanner.schema :members: :inherited-members: - - + \ No newline at end of file diff --git a/docs/schema-usage.rst b/docs/schema-usage.rst deleted file mode 100644 index 451813b498..0000000000 --- a/docs/schema-usage.rst +++ /dev/null @@ -1,4 +0,0 @@ -Schema -#################################### - -[this page is under construction] diff --git a/docs/utils-api.rst b/docs/utils-api.rst new file mode 100644 index 0000000000..83ade06899 --- /dev/null +++ b/docs/utils-api.rst @@ -0,0 +1,7 @@ +Utils API +===================== + +.. automodule:: django_spanner.utils + :members: + :inherited-members: + \ No newline at end of file