禁用Django数据库写入。
项目描述
禁用Django数据库写入。
更智能、更快速地工作,使用我的书籍Boost Your Django DX,涵盖了django-read-only、IPython和其他许多工具。
需求
支持Python 3.8到3.12。
支持Django 3.2到5.1。
安装
使用
pip
安装python -m pip install django-read-only
然后将它添加到您的已安装应用中
INSTALLED_APPS = [
...,
"django_read_only",
...,
]
用法
在您的设置文件中,将DJANGO_READ_ONLY设置为True,所有数据修改查询将引发异常
$ DJANGO_READ_ONLY=1 python manage.py shell
...
>>> User.objects.create_user(username="hacker", password="hunter2")
...
DjangoReadOnlyError(...)
为了方便起见,您也可以使用DJANGO_READ_ONLY环境变量来控制此操作,如果设置为非空字符串,则视为True。设置优先于环境变量。
在DJANGO_READ_ONLY设置为开启的会话中,您可以通过调用enable_writes()来重新启用写入
>>> import django_read_only
>>> django_read_only.enable_writes()
可以使用 disable_writes() 禁用写入。
>>> django_read_only.disable_writes()
要临时允许写入,请使用 temp_writes() 上下文管理器/装饰器。
>>> with django_read_only.temp_writes():
... User.objects.create_user(...)
...
请注意,启用/禁用写入是全局状态,会影响所有线程和异步协程。
推荐设置
在生产环境和可能处于交互会话的预发布环境中设置只读模式。这可以通过设置系统用户账户的shell配置文件(如bashrc、zshrc等)中的 DJANGO_READ_ONLY 环境变量来实现。这样,进行探索性查询的开发者就不能意外地进行更改,但非shell进程(如您的WSGI服务器)的写入仍然会被启用。
使用此设置,开发人员也可以通过在命令之前设置环境变量来运行带有写入启用的管理命令。
$ DJANGO_READ_ONLY= python manage.py clearsessions
一些部署平台不允许您自定义shell配置文件。在这种情况下,您需要找到一种方法从您的设置文件中检测shell模式。
例如,在Heroku上,有 DYNO 环境变量(文档)用于识别当前虚拟机。对于交互会话,它以“run.”开头。您可以在设置文件中使用此信息来启用只读模式,如下所示:
if os.environ.get("DYNO", "").startswith("run."):
DJANGO_READ_ONLY = bool(os.environ.get("DJANGO_READ_ONLY", "1"))
else:
DJANGO_READ_ONLY = False
IPython扩展
django-read-only还可以作为IPython扩展快速启用/禁用只读模式。通过以下方式加载:
In [1]: %load_ext django_read_only
您可以通过设置到您的IPython配置文件来让扩展始终加载。
c.InteractiveShellApp.extensions.append("django_read_only")
加载后,使用 %read_only 行魔法来禁用或启用只读模式
In [2]: %read_only off
Write queries enabled.
In [3]: %read_only on
Write queries disabled.
这减少了禁用只读模式所需的输入量。
工作原理
防止写入最准确的方法是以只有读取权限的单独数据库用户连接。然而,这有一些限制 - Django不支持实时修改 DATABASES 设置,因此会话将无法暂时允许写入。
相反,django-read-only使用始终安装的数据库仪表板来检查执行的查询,并仅允许看起来像读取的查询。它采用“关闭失败”的哲学,因此任何未知的内容都会失败,这在理论上应该是合理的。
由于django-read-only使用Django数据库仪表板,因此它不能阻止通过底层数据库连接(通过 django.db.connection.connection 访问)运行的查询,并且它不能过滤存储过程(使用 connection.callproc())内的操作。虽然在实践中这些操作非常罕见,但django-read-only的方法对大多数项目来说都很有用。