Package: django-db-readonly
django-db-readonlyAboutA way to globally disable writes to your database. This works by
inserting a cursor wrapper between Django'sCursorWrapperand the
database connection's cursor wrapper.InstallationInstall with:>pipinstalldjango-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 writesSITE_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=(# ...'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:# EnableDB_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 %}ConfigurationSITE_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.TestingTests are pretty basic, right now.CaveatsThis will work withDjango Debug Toolbar. In fact, I was inspired byDjDT'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.CopyrightCopyright © 2020, Chris Streeter under the MIT software license. See LICENSE for more information.