Package: ambition-django-db-readonly
AboutA way to globally disable writes to your database. This works by
inserting a cursor wrapper between Django’sCursorWrapperand the
database connection’s cursor wrapper.InstallationThe library is hosted onPyPi, so you can
grab it there with:pip install django-db-readonlyThen addreadonlyto yourINSTALLED_APPS.:INSTALLED_APPS = (
    # ...
    'readonly',
    # ...
)UsageYou need to add this line to yoursettings.pyto make the database read-only:# Set to False to allow writes
SITE_READ_ONLY = TrueWhen 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 anerror 500
page.
The exception you will want to catch isreadonly.exceptions.DatabaseWriteDeniedwhich inherits fromdjango.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 yoursettings.py:MIDDLEWARE_CLASSES = (
    # ...
    'readonly.middleware.DatabaseReadOnlyMiddleware',
    # ...
)This will then catchDatabaseWriteDeniedexceptions. 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 aHttpResponsewith text telling the user the site is in read-only
mode.In addition, the middleware class can add an error-type message using
thedjango.contrib.messagesmodule. Add:# Enable
DB_READ_ONLY_MIDDLEWARE_MESSAGE = Trueto yoursettings.pyand then on POST requests that generate aDatabaseWriteDeniedexception, 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 addsSITE_READ_ONLYinto the context. Add the following line in yoursettings.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 %}TestingThere aren’t any tests included, yet. Run it at your own risk.CaveatsThis will work withDjango Debug
Toolbar. In fact,
I was inspired byDDT’s sql
panelwhen writing this app.However, in order for both DDTanddjango-db-readonly to work, you
need to make sure that you havereadonlybeforedebug_toolbarin
yourINSTALLED_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 eitherdjango.db.backends.util.CursorWrapperordjango.db.backends.util.CursorDebugWrapper, you need to make sure
thatreadonlyis placedbeforeof those apps inINSTALLED_APPS.The Nitty GrittyHow does this do what it does? Well, django-db-readonly sits between
Django’s own cursor wrapper atdjango.db.backends.util.CursorWrapperand the database specific cursor atdjango.db.backends.*.base.*CursorWrapper. It overrides two specific
methods:executeandexecutemany. If the site is in read-only
mode, then the SQL is examined to see if it contains any write actions
(defined inreadonly.ReadOnlyCursorWrapper.SQL_WRITE_BLACKLIST). If
a write is detected, an exception is raised.LicenseUses theMITlicense.