Pure serverless Django with django-gcloud-connectors

Katie McLaughlin - Jul 7 '20 - - Dev Community

Django officially supports a number of database backends -- including PostgreSQL, MySQL, and SQLite -- which means you can write your Django project once and deploy it to any of these database backends.

But, there are third-party packages to support other databases, even non-relational databases like Google Datastore.

In this project, I used django-gcloud-connectors, newly released on pypi as a 0.1.0 from Potato London, to make a Django deployment on Google Cloud completely serverless (and in theory, free!)

Sample project code → github.com/glasnt/mewgram

Sample project screenshot ⤵
sample screenshot


I spoke at my first DjangoCon Europe back in 2016 in Budapest, where I saw an intriguing talk by Adam Alton: Building A Non-Relational Backend For The ORM (video). His talk centered around djangae, which allows vanilla Django sites deployed to App Engine to use Datastore, a highly scalable NoSQL database (currently referred to as "Firestore in Datastore Mode").

However, djangae -- as the name suggests with the GAE at the end -- requires Google App Engine. However, I'm a containers girl and I prefer Cloud Run, so I thought, how hard could it be to use the bits of djangae just to get the database backend into Datastore working?

Turns out, it's not hard when djangae itself uses django-gcloud-connectors.

By installing and integrating django-gcloud-connectors into a sufficiently simple Django app, and with some learning about how indexes in Datastore work, I was able to put together a proof of concept that deploys using the Cloud Run Button onto Google Cloud Run, allowing you to deploy this sample app and see it working today.


But why do you need a purpose-built backend connector?

You can use google-cloud-datastore to interact directly to Datastore with Python. However, if you want to take advantage of Django's ORM, you need to use a connection that maps Django's models to that specific database. The officially supported databases are officially supported for a reason -- the eccentricities of having a generic ORM interfacing to any relational database is not easy, and keeping them up to date for popular databases is important.

The folks at Potato have -- incredibly -- made Django (mostly) work on a non-relational database. I say mostly for a reason -- there are some limitations with Datastore itself which means some of the more complex functionality that is taken for granted in a relational database just isn't available for Datastore, and as such djangae and django-gcloud-connectors cannot support it. I came across issues where cross-join where filters were not supported when trying to migrate unicodex. Take a look at the NotSupportedError exceptions in parser/base.py for more examples.


Did you say free?

Well, yes, mostly. While you do need to have billing enabled to deploy mewgram, Datastore itself has a generous free tier, as do the other elements involved. I normally use Postgres for my Django demos, like unicodex, and the smallest Cloud SQL database has a non-zero monthly cost. In theory, unless your application gets super popular, you should have no ongoing costs running a Django project backed to Datastore. As always, consider the costs involved and setup billing alerting for long-running projects!


Cats?

Yup, the identicons are provided by robohash, a Python package that has a cat setting.


Why connector*s*?

The package is called django-gcloud-connector*s* -- plural -- the project README currently only supports Firestore in Datastore mode, but hopes to support Firestore (in Native mode) in the future. With Django 3.1 slated to introduce even more ASGI compatibility, I look forward to hopefully one day having a full asynchronous Django, with a realtime database to boot.


You can learn more about django-gcloud-connectors at gitlab.com/potato-oss/google-cloud/django-gcloud-connectors, and Potato London at https://p.ota.to/.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .