Package: django-read-only
Disable Django database writes.RequirementsPython 3.8 to 3.12 supported.Django 3.2 to 5.0 supported.Want to work smarter and faster?Check out my bookBoost Your Django DXwhich covers django-read-only, IPython, and many other tools.InstallationInstall withpip:python-mpipinstalldjango-read-onlyThen add to your installed apps:INSTALLED_APPS=[...,"django_read_only",...,]UsageIn your settings file, setDJANGO_READ_ONLYtoTrueand all data modification queries will cause an exception:$DJANGO_READ_ONLY=1pythonmanage.pyshell...
>>> User.objects.create_user(username="hacker", password="hunter2")
...
DjangoReadOnlyError(...)For convenience, you can also control this with theDJANGO_READ_ONLYenvironment variable, which will count asTrueif set to anything but the empty string.
The setting takes precedence over the environment variable.During a session withDJANGO_READ_ONLYset on, you can re-enable writes by callingenable_writes():>>>importdjango_read_only>>>django_read_only.enable_writes()Writes can be disabled withdisable_writes():>>>django_read_only.disable_writes()To temporarily allow writes, use thetemp_writes()context manager / decorator:>>>withdjango_read_only.temp_writes():...User.objects.create_user(...)...Note that writes being enabled/disabled is global state, affecting all threads and asynchronous coroutines.Recommended SetupSet read-only mode on in your production environment, and maybe staging, during interactive sessions.
This can be done by setting theDJANGO_READ_ONLYenvironment variable in the shell profile file (bashrc,zshrc, etc.) of the system’s user account.
This way developers performing exploratory queries can’t accidentally make changes, but writes will remain enabled for non-shell processes like your WSGI server.With this setup, developers can also run management commands with writes enabled by setting the environment variable before the command:$DJANGO_READ_ONLY=pythonmanage.pyclearsessionsSome deployment platforms don’t allow you to customize your shell profile files.
In this case, you will need to find a way to detect shell mode from within your settings file.For example, on Heroku there’s theDYNOenvironment variable (docs) to identify the current virtual machine.
It starts with “run.” for interactive sessions.
You can use this to enable read-only mode in your settings file like so:ifos.environ.get("DYNO","").startswith("run."):DJANGO_READ_ONLY=bool(os.environ.get("DJANGO_READ_ONLY","1"))else:DJANGO_READ_ONLY=FalseIPython Extensiondjango-read-only also works as an IPython extension for quick access to enable/disable read-only mode.
Load it with:In [1]: %load_ext django_read_onlyYou can have the extension always load by setting it up to yourIPython configuration file:c.InteractiveShellApp.extensions.append("django_read_only")When loaded, use the%read_onlyline magic to disable or enable read-only mode:In [2]: %read_only off
Write queries enabled.

In [3]: %read_only on
Write queries disabled.This reduces the amount of typing needed to disable read-only mode.How it WorksThe most accurate way to prevent writes is to connect as a separate database user with only read permission.
However, this has limitations - Django doesn’t support modifying theDATABASESsetting live, so sessions would not be able to temporarily allow writes.Instead, django-read-only usesalways installed database instrumentationto inspect executed queries and only allow those which look like reads.
It uses a “fail closed” philosophy, so anything unknown will fail, which should be fairly reasonable.Because django-read-only uses Django database instrumentation, it cannot block queries running through the underlying database connection (accesses throughdjango.db.connection.connection), and it cannot filter operations within stored procedures (which useconnection.callproc()).
These are very rare in practice though, so django-read-only’s method works well for most projects.