Name | django-read-only JSON |
Version |
1.19.0
JSON |
| download |
home_page | None |
Summary | Disable Django database writes. |
upload_time | 2025-02-06 22:19:13 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.9 |
license | None |
keywords |
django
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
================
django-read-only
================
.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/django-read-only/main.yml.svg?branch=main&style=for-the-badge
:target: https://github.com/adamchainz/django-read-only/actions?workflow=CI
.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge
:target: https://github.com/adamchainz/django-read-only/actions?workflow=CI
.. image:: https://img.shields.io/pypi/v/django-read-only.svg?style=for-the-badge
:target: https://pypi.org/project/django-read-only/
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge
:target: https://github.com/psf/black
.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge
:target: https://github.com/pre-commit/pre-commit
:alt: pre-commit
Disable Django database writes.
----
**Work smarter and faster** with my book `Boost Your Django DX <https://adamchainz.gumroad.com/l/byddx>`__ which covers django-read-only, IPython, and many other tools.
----
Requirements
------------
Python 3.9 to 3.13 supported.
Django 4.2 to 5.2 supported.
Installation
------------
Install with **pip**:
.. code-block:: sh
python -m pip install django-read-only
Then add to your installed apps:
.. code-block:: python
INSTALLED_APPS = [
...,
"django_read_only",
...,
]
Usage
-----
In your settings file, set ``DJANGO_READ_ONLY`` to ``True`` and all data modification queries will cause an exception:
.. code-block:: console
$ DJANGO_READ_ONLY=1 python manage.py shell
...
>>> User.objects.create_user(username="hacker", password="hunter2")
...
DjangoReadOnlyError(...)
For convenience, you can also control this with the ``DJANGO_READ_ONLY`` environment variable, which will count as ``True`` if set to anything but the empty string.
The setting takes precedence over the environment variable.
During a session with ``DJANGO_READ_ONLY`` set on, you can re-enable writes by calling ``enable_writes()``:
.. code-block:: pycon
>>> import django_read_only
>>> django_read_only.enable_writes()
Writes can be disabled with ``disable_writes()``:
.. code-block:: pycon
>>> django_read_only.disable_writes()
To temporarily allow writes, use the ``temp_writes()`` context manager / decorator:
.. code-block:: pycon
>>> with django_read_only.temp_writes():
... User.objects.create_user(...)
...
Note that writes being enabled/disabled is global state, affecting all threads and asynchronous coroutines.
Recommended Setup
-----------------
Set read-only mode on in your production environment, and maybe staging, during interactive sessions.
This can be done by setting the ``DJANGO_READ_ONLY`` environment 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:
.. code-block:: console
$ DJANGO_READ_ONLY= python manage.py clearsessions
Some 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 the ``DYNO`` environment variable (`docs <https://devcenter.heroku.com/articles/dynos#local-environment-variables>`__) 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:
.. code-block:: python
if os.environ.get("DYNO", "").startswith("run."):
DJANGO_READ_ONLY = bool(os.environ.get("DJANGO_READ_ONLY", "1"))
else:
DJANGO_READ_ONLY = False
IPython Extension
-----------------
django-read-only also works as an IPython extension for quick access to enable/disable read-only mode.
Load it with:
.. code-block:: ipython
In [1]: %load_ext django_read_only
You can have the extension always load by setting it up to your `IPython configuration file <https://ipython.readthedocs.io/en/stable/config/intro.html>`__:
.. code-block:: python
c.InteractiveShellApp.extensions.append("django_read_only")
When loaded, use the ``%read_only`` line magic to disable or enable read-only mode:
.. code-block:: ipython
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 Works
------------
The 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 the ``DATABASES`` setting live, so sessions would not be able to temporarily allow writes.
Instead, django-read-only uses `always installed database instrumentation <https://adamj.eu/tech/2020/07/23/how-to-make-always-installed-django-database-instrumentation/>`__ to 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 through ``django.db.connection.connection``), and it cannot filter operations within stored procedures (which use ``connection.callproc()``).
These are very rare in practice though, so django-read-only’s method works well for most projects.
Raw data
{
"_id": null,
"home_page": null,
"name": "django-read-only",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "Django",
"author": null,
"author_email": "Adam Johnson <me@adamj.eu>",
"download_url": "https://files.pythonhosted.org/packages/7d/bd/ac53e96df4f54e705d7a48435cf213e7d82ece461851b22eca5632615fa1/django_read_only-1.19.0.tar.gz",
"platform": null,
"description": "================\ndjango-read-only\n================\n\n.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/django-read-only/main.yml.svg?branch=main&style=for-the-badge\n :target: https://github.com/adamchainz/django-read-only/actions?workflow=CI\n\n.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge\n :target: https://github.com/adamchainz/django-read-only/actions?workflow=CI\n\n.. image:: https://img.shields.io/pypi/v/django-read-only.svg?style=for-the-badge\n :target: https://pypi.org/project/django-read-only/\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge\n :target: https://github.com/psf/black\n\n.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge\n :target: https://github.com/pre-commit/pre-commit\n :alt: pre-commit\n\nDisable Django database writes.\n\n----\n\n**Work smarter and faster** with my book `Boost Your Django DX <https://adamchainz.gumroad.com/l/byddx>`__ which covers django-read-only, IPython, and many other tools.\n\n----\n\nRequirements\n------------\n\nPython 3.9 to 3.13 supported.\n\nDjango 4.2 to 5.2 supported.\n\nInstallation\n------------\n\nInstall with **pip**:\n\n.. code-block:: sh\n\n python -m pip install django-read-only\n\nThen add to your installed apps:\n\n.. code-block:: python\n\n INSTALLED_APPS = [\n ...,\n \"django_read_only\",\n ...,\n ]\n\nUsage\n-----\n\nIn your settings file, set ``DJANGO_READ_ONLY`` to ``True`` and all data modification queries will cause an exception:\n\n.. code-block:: console\n\n $ DJANGO_READ_ONLY=1 python manage.py shell\n ...\n >>> User.objects.create_user(username=\"hacker\", password=\"hunter2\")\n ...\n DjangoReadOnlyError(...)\n\nFor convenience, you can also control this with the ``DJANGO_READ_ONLY`` environment variable, which will count as ``True`` if set to anything but the empty string.\nThe setting takes precedence over the environment variable.\n\nDuring a session with ``DJANGO_READ_ONLY`` set on, you can re-enable writes by calling ``enable_writes()``:\n\n.. code-block:: pycon\n\n >>> import django_read_only\n >>> django_read_only.enable_writes()\n\nWrites can be disabled with ``disable_writes()``:\n\n.. code-block:: pycon\n\n >>> django_read_only.disable_writes()\n\nTo temporarily allow writes, use the ``temp_writes()`` context manager / decorator:\n\n.. code-block:: pycon\n\n >>> with django_read_only.temp_writes():\n ... User.objects.create_user(...)\n ...\n\nNote that writes being enabled/disabled is global state, affecting all threads and asynchronous coroutines.\n\nRecommended Setup\n-----------------\n\nSet read-only mode on in your production environment, and maybe staging, during interactive sessions.\nThis can be done by setting the ``DJANGO_READ_ONLY`` environment variable in the shell profile file (``bashrc``, ``zshrc``, etc.) of the system\u2019s user account.\nThis way developers performing exploratory queries can\u2019t accidentally make changes, but writes will remain enabled for non-shell processes like your WSGI server.\n\nWith this setup, developers can also run management commands with writes enabled by setting the environment variable before the command:\n\n.. code-block:: console\n\n $ DJANGO_READ_ONLY= python manage.py clearsessions\n\nSome deployment platforms don\u2019t allow you to customize your shell profile files.\nIn this case, you will need to find a way to detect shell mode from within your settings file.\n\nFor example, on Heroku there\u2019s the ``DYNO`` environment variable (`docs <https://devcenter.heroku.com/articles/dynos#local-environment-variables>`__) to identify the current virtual machine.\nIt starts with \u201crun.\u201d for interactive sessions.\nYou can use this to enable read-only mode in your settings file like so:\n\n.. code-block:: python\n\n if os.environ.get(\"DYNO\", \"\").startswith(\"run.\"):\n DJANGO_READ_ONLY = bool(os.environ.get(\"DJANGO_READ_ONLY\", \"1\"))\n else:\n DJANGO_READ_ONLY = False\n\nIPython Extension\n-----------------\n\ndjango-read-only also works as an IPython extension for quick access to enable/disable read-only mode.\nLoad it with:\n\n.. code-block:: ipython\n\n In [1]: %load_ext django_read_only\n\nYou can have the extension always load by setting it up to your `IPython configuration file <https://ipython.readthedocs.io/en/stable/config/intro.html>`__:\n\n.. code-block:: python\n\n c.InteractiveShellApp.extensions.append(\"django_read_only\")\n\nWhen loaded, use the ``%read_only`` line magic to disable or enable read-only mode:\n\n.. code-block:: ipython\n\n In [2]: %read_only off\n Write queries enabled.\n\n In [3]: %read_only on\n Write queries disabled.\n\nThis reduces the amount of typing needed to disable read-only mode.\n\nHow it Works\n------------\n\nThe most accurate way to prevent writes is to connect as a separate database user with only read permission.\nHowever, this has limitations - Django doesn\u2019t support modifying the ``DATABASES`` setting live, so sessions would not be able to temporarily allow writes.\n\nInstead, django-read-only uses `always installed database instrumentation <https://adamj.eu/tech/2020/07/23/how-to-make-always-installed-django-database-instrumentation/>`__ to inspect executed queries and only allow those which look like reads.\nIt uses a \u201cfail closed\u201d philosophy, so anything unknown will fail, which should be fairly reasonable.\n\nBecause django-read-only uses Django database instrumentation, it cannot block queries running through the underlying database connection (accesses through ``django.db.connection.connection``), and it cannot filter operations within stored procedures (which use ``connection.callproc()``).\nThese are very rare in practice though, so django-read-only\u2019s method works well for most projects.\n",
"bugtrack_url": null,
"license": null,
"summary": "Disable Django database writes.",
"version": "1.19.0",
"project_urls": {
"Changelog": "https://github.com/adamchainz/django-read-only/blob/main/CHANGELOG.rst",
"Funding": "https://adamj.eu/books/",
"Repository": "https://github.com/adamchainz/django-read-only"
},
"split_keywords": [
"django"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a27f73ce6e07eb8af03a4b23eba2363f23561a1dbcc46db286817e39bc4d32d7",
"md5": "db223bffc24619e1fcc5a549289ae5d8",
"sha256": "9d31f476d841d72186b2e206299600f7dd62e7603c4ad915c19653403f1e4ce1"
},
"downloads": -1,
"filename": "django_read_only-1.19.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "db223bffc24619e1fcc5a549289ae5d8",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 6637,
"upload_time": "2025-02-06T22:19:11",
"upload_time_iso_8601": "2025-02-06T22:19:11.310441Z",
"url": "https://files.pythonhosted.org/packages/a2/7f/73ce6e07eb8af03a4b23eba2363f23561a1dbcc46db286817e39bc4d32d7/django_read_only-1.19.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "7dbdac53e96df4f54e705d7a48435cf213e7d82ece461851b22eca5632615fa1",
"md5": "ce87f328d45e5f19e6abf1d0089e3df8",
"sha256": "173e3b74347e01885bbd5e92e36d2d5bcaf59625151363011369d90b0c632e51"
},
"downloads": -1,
"filename": "django_read_only-1.19.0.tar.gz",
"has_sig": false,
"md5_digest": "ce87f328d45e5f19e6abf1d0089e3df8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 7748,
"upload_time": "2025-02-06T22:19:13",
"upload_time_iso_8601": "2025-02-06T22:19:13.551350Z",
"url": "https://files.pythonhosted.org/packages/7d/bd/ac53e96df4f54e705d7a48435cf213e7d82ece461851b22eca5632615fa1/django_read_only-1.19.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-06 22:19:13",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "adamchainz",
"github_project": "django-read-only",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "django-read-only"
}