Skip to main content

django-db-readonly

About

A way to globally disable writes to your database. This works by inserting a cursor wrapper between Django's CursorWrapper and the database connection's cursor wrapper.

Installation

Install with:

> pip install django-db-readonly

Then add readonly to your INSTALLED_APPS.

INSTALLED_APPS = (
    # ...
    'readonly',
    # ...
)

Usage

You need to add this line to your settings.py to make the database read-only:

# Set to False to allow writes
SITE_READ_ONLY = True

When you do this, any write action to your databases will generate an exception. You should catch this exception and deal with it somehow. Or let Django display an error 500 page. The exception you will want to catch is readonly.exceptions.DatabaseWriteDenied which inherits from django.db.utils.DatabaseError.

There is also a middleware class that will handle the exceptions and attempt to handle them as explained below. To enable the middleware, add the following line to your settings.py:

MIDDLEWARE = (
    # ...
    'readonly.middleware.DatabaseReadOnlyMiddleware',
    # ...
)

This will then catch DatabaseWriteDenied exceptions. If the request is a POST request, we will redirect the user to the same URL, but as a GET request. If the request is not a POST (ie. a GET), we will just display a HttpResponse with text telling the user the site is in read-only mode.

In addition, the middleware class can add an error-type message using the django.contrib.messages module. Add:

# Enable
DB_READ_ONLY_MIDDLEWARE_MESSAGE = True

to your settings.py and then on POST requests that generate a DatabaseWriteDenied exception, we will add an error message informing the user that the site is in read-only mode.

For additional messaging, there is a context processor that adds SITE_READ_ONLY into the context. Add the following line in your settings.py:

TEMPLATE_CONTEXT_PROCESSORS = (
    # ...
    'readonly.context_processors.readonly',
    # ...
)

And use it as you would any boolean in the template, e.g. {% if SITE_READ_ONLY %}We're down for maintenance.{% endif %}

Configuration

  • SITE_READ_ONLY - Use to disable writes to the database.
  • DB_READ_ONLY_DATABASES - A list of database names that read only is enforced on (and ignored for others).
  • DB_READ_ONLY_MIDDLEWARE_MESSAGE - A custom message that can be used to tell the user when the DB is in readonly mode.

Testing

Tests are pretty basic, right now.

Caveats

This will work with Django Debug Toolbar. In fact, I was inspired by DjDT's sql panel when writing this app.

However, in order for both DDT and django-db-readonly to work, you need to make sure that you have readonly before debug_toolbar in your INSTALLED_APPS. Otherwise, you are responsible for debugging what is going on. Of course, I'm not sure why you'd be running DDT in production and running django-db-readonly in development, but whatever, I'm not you.

More generally, if you have any other apps that modifies either django.db.backends.util.CursorWrapper or django.db.backends.util.CursorDebugWrapper, you need to make sure that readonly is placed before of those apps in INSTALLED_APPS.

The Nitty Gritty

How does this do what it does? Well, django-db-readonly sits between Django's own cursor wrapper at django.db.backends.util.CursorWrapper and the database specific cursor at django.db.backends.*.base.*CursorWrapper. It overrides two specific methods: execute and executemany. If the site is in read-only mode, then the SQL is examined to see if it contains any write actions (defined in readonly.ReadOnlyCursorWrapper.SQL_WRITE_BLACKLIST). If a write is detected, an exception is raised.

CircleCI PyPI version

Copyright

Copyright © 2020, Chris Streeter under the MIT software license. See LICENSE for more information.

Release files for django-db-readonly 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-db-readonly 0.7.0
File Size Uploaded
django-db-readonly-0.7.0.tar.gz 5.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-db-readonly 0.7.0
File Interpreter ABI Platform
django_db_readonly-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 13.3 kB

Release files / django-db-readonly-0.7.0.tar.gz

Download URL django-db-readonly-0.7.0.tar.gz
Size 5.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e164313ec65af1ceb15007035d9e8a27564c4b993352a7394dc63c36ef6dd77a
BLAKE2b-256 checksum
How to use checksums
d926741fe928d327170a20c20a2398b438ec7c7ac173913fe762399ebd4a66b2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.0.0 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.8.5

Release files / django_db_readonly-0.7.0-py3-none-any.whl

Download URL django_db_readonly-0.7.0-py3-none-any.whl
Size 7.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3e7f335baea5072f3e0e8fdb3df6805daab7158781f7f0cc14b7eff96571ca25
BLAKE2b-256 checksum
How to use checksums
068b8c770e62bfcada7eed76b29ae6582e040ba26174b8b21e6a21634953a528
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/50.0.0 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.8.5

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4

2 release files

0.3.3

2 release files

0.3.2

1 release file

0.3.1

1 release file

0.3.0

1 release file

0.2.0

1 release file

0.1.1

1 release file

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page