Git Product home page Git Product logo

django-db-readonly's Introduction

django-db-readonly

About

A way to globally disable writes to your database. This works by inserting a cursor wrapper between Django's CursorWrapper and the database connection's cursor wrapper.

Installation

Install with:

> pip install django-db-readonly

Then add readonly to your INSTALLED_APPS.

INSTALLED_APPS = (
    # ...
    'readonly',
    # ...
)

Usage

You need to add this line to your settings.py to make the database read-only:

# Set to False to allow writes
SITE_READ_ONLY = True

When 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 an error 500 page. The exception you will want to catch is readonly.exceptions.DatabaseWriteDenied which inherits from django.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 your settings.py:

MIDDLEWARE = (
    # ...
    'readonly.middleware.DatabaseReadOnlyMiddleware',
    # ...
)

This will then catch DatabaseWriteDenied exceptions. 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 a HttpResponse with text telling the user the site is in read-only mode.

In addition, the middleware class can add an error-type message using the django.contrib.messages module. Add:

# Enable
DB_READ_ONLY_MIDDLEWARE_MESSAGE = True

to your settings.py and then on POST requests that generate a DatabaseWriteDenied exception, 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 adds SITE_READ_ONLY into the context. Add the following line in your settings.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 %}

Configuration

  • SITE_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.

Testing

Tests are pretty basic, right now.

Caveats

This will work with Django Debug Toolbar. In fact, I was inspired by DjDT's sql panel when writing this app.

However, in order for both DDT and django-db-readonly to work, you need to make sure that you have readonly before debug_toolbar in your INSTALLED_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 either django.db.backends.util.CursorWrapper or django.db.backends.util.CursorDebugWrapper, you need to make sure that readonly is placed before of those apps in INSTALLED_APPS.

The Nitty Gritty

How does this do what it does? Well, django-db-readonly sits between Django's own cursor wrapper at django.db.backends.util.CursorWrapper and the database specific cursor at django.db.backends.*.base.*CursorWrapper. It overrides two specific methods: execute and executemany. If the site is in read-only mode, then the SQL is examined to see if it contains any write actions (defined in readonly.ReadOnlyCursorWrapper.SQL_WRITE_BLACKLIST). If a write is detected, an exception is raised.

CircleCI PyPI version

Copyright

Copyright © 2020, Chris Streeter under the MIT software license. See LICENSE for more information.

django-db-readonly's People

Contributors

artscoop avatar avelis avatar bartek avatar garciparedes avatar igniteflow avatar kevcampb avatar kiarashplusplus avatar staticshock avatar streeter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

django-db-readonly's Issues

DatabaseReadOnlyMiddleware not compatible with new Django new-style MIDDLEWARE

The following settings directive

MIDDLEWARE_CLASSES = (
    # ...
    'readonly.middleware.DatabaseReadOnlyMiddleware',
    # ...
)

Doesn't work for the new Django 1.10 MIDDLEWARE new-style setup.

MIDDLEWARE = [
    # ...
    'readonly.middleware.DatabaseReadOnlyMiddleware',
]

See https://docs.djangoproject.com/en/1.10/releases/1.10/#new-style-middleware

You end up with an error like this:

  File "/.../appname/wsgi.py", line 14, in <module>
    application = get_wsgi_application()
  File "/.../pythonenv/python3.5/lib/python3.5/site-packages/django/core/wsgi.py", line 14, in get_wsgi_application
    return WSGIHandler()
  File "/.../pythonenv/python3.5/lib/python3.5/site-packages/django/core/handlers/wsgi.py", line 153, in __init__
    self.load_middleware()
  File "/.../pythonenv/python3.5/lib/python3.5/site-packages/django/core/handlers/base.py", line 82, in load_middleware
    mw_instance = middleware(handler)
TypeError: object() takes no parameters

TypeError: 'dict' object is not callable

Traceback:

File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

Exception Type: TypeError at /admin/
Exception Value: 'dict' object is not callable

Failing line context:

    This decorator is automatically applied to all middleware to ensure that
    no middleware leaks an exception and that the next middleware in the stack
    can rely on getting a response instead of an exception.
    """
    @wraps(get_response)
    def inner(request):
        try:
            response = get_response(request) …
        except Exception as exc:
            response = response_for_exception(request, exc)
        return response
    return inner

Local vars:

exc: TypeError("'dict' object is not callable")
get_response: {'SITE_READ_ONLY': False}
request: <WSGIRequest: GET '/admin/'>

Environment:

Request Method: GET
Request URL: http://127.0.0.1:8000/admin/

Django Version: 2.2.5
Python Version: 3.7.0
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django_extensions',
 'logentry_admin',
 'rest_framework',
 'rest_framework.authtoken',
 'anymail',
 'phoenix',
 'api',
 'readonly']
Installed Middleware:
['whitenoise.middleware.WhiteNoiseMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'readonly.context_processors.readonly']

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.