From 4bb3a78059a1412b1b784c61bd6d7aad62f43157 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Thu, 2 Jan 2025 12:45:49 +0100 Subject: [PATCH] Split read-only and read/write API by the db backends --- nopayloaddb/db_router.py | 29 +++++++++++++++++++++++ nopayloaddb/middleware.py | 20 ++++++++++++++++ nopayloaddb/settings.py | 49 ++++++++++++++++++++++++++++++++++----- 3 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 nopayloaddb/db_router.py create mode 100644 nopayloaddb/middleware.py diff --git a/nopayloaddb/db_router.py b/nopayloaddb/db_router.py new file mode 100644 index 0000000..e8e8cf8 --- /dev/null +++ b/nopayloaddb/db_router.py @@ -0,0 +1,29 @@ +import random +from nopayloaddb.middleware import get_current_request + +logger = logging.getLogger(__name__) + +class ReadWriteRouter: + def db_for_read(self, model, **hints): + """Route read queries to one of the read databases.""" + db = random.choice(['read_db_1', 'read_db_2']) + # Check if request information is available + request = get_current_request() + if request and request.method == 'GET': + return db + return 'default' + + def db_for_write(self, model, **hints): + """Route write queries to the default database.""" + return 'default' + + def allow_relation(self, obj1, obj2, **hints): + """Allow any relation if both objects are in the same database.""" + db_set = {'default', 'read_db_1', 'read_db_2'} + if obj1._state.db in db_set and obj2._state.db in db_set: + return True + return None + + def allow_migrate(self, db, app_label, model_name=None, **hints): + """Ensure migrations only run on the default database.""" + return db == 'default' diff --git a/nopayloaddb/middleware.py b/nopayloaddb/middleware.py new file mode 100644 index 0000000..cfb90d6 --- /dev/null +++ b/nopayloaddb/middleware.py @@ -0,0 +1,20 @@ +from threading import local + +# Local storage for storing the current request +_request_local = local() + +def get_current_request(): + return getattr(_request_local, 'request', None) + +class RequestMiddleware: + """Middleware to store the current request for use in the DB router.""" + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + # Save the request in the local thread storage + _request_local.request = request + response = self.get_response(request) + # Clear the request after processing to avoid conflicts + _request_local.request = None + return response \ No newline at end of file diff --git a/nopayloaddb/settings.py b/nopayloaddb/settings.py index bc84ce8..cc9211d 100644 --- a/nopayloaddb/settings.py +++ b/nopayloaddb/settings.py @@ -53,6 +53,8 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + #My middleware + 'nopayloaddb.middleware.RequestMiddleware', ] ROOT_URLCONF = 'nopayloaddb.urls' @@ -116,20 +118,55 @@ # } #} +#DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.postgresql_psycopg2', +# +# +# 'NAME': os.environ.get("POSTGRES_DB", default='dbname'), +# 'USER': os.environ.get("POSTGRES_USER", default='login'), +# 'PASSWORD': os.environ.get("POSTGRES_PASSWORD", default='password'), +# 'HOST': os.environ.get("POSTGRES_HOST", default='localhost'), +# 'PORT': os.environ.get("POSTGRES_PORT", default='5432'), +# +# } +#} + DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': os.environ.get("POSTGRES_DB", default='dbname'), - 'USER': os.environ.get("POSTGRES_USER", default='login'), - 'PASSWORD': os.environ.get("POSTGRES_PASSWORD", default='password'), - 'HOST': os.environ.get("POSTGRES_HOST", default='localhost'), - 'PORT': os.environ.get("POSTGRES_PORT", default='5432'), + 'NAME': os.environ.get("POSTGRES_DB_W", default='dbname'), + 'USER': os.environ.get("POSTGRES_USER_W", default='login'), + 'PASSWORD': os.environ.get("POSTGRES_PASSWORD_W", default='password'), + 'HOST': os.environ.get("POSTGRES_HOST_W", default='localhost'), + 'PORT': os.environ.get("POSTGRES_PORT_W", default='5432'), + }, + 'read_db_1': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + + + 'NAME': os.environ.get("POSTGRES_DB_R1", default='dbname'), + 'USER': os.environ.get("POSTGRES_USER_R1", default='login'), + 'PASSWORD': os.environ.get("POSTGRES_PASSWORD_R1", default='password'), + 'HOST': os.environ.get("POSTGRES_HOST_R1", default='localhost'), + 'PORT': os.environ.get("POSTGRES_PORT_R1", default='5432'), + }, + 'read_db_2': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', - } + + 'NAME': os.environ.get("POSTGRES_DB_R2", default='dbname'), + 'USER': os.environ.get("POSTGRES_USER_R2", default='login'), + 'PASSWORD': os.environ.get("POSTGRES_PASSWORD_R2", default='password'), + 'HOST': os.environ.get("POSTGRES_HOST_R2", default='localhost'), + 'PORT': os.environ.get("POSTGRES_PORT_R2", default='5432'), + }, } +DATABASE_ROUTERS = ['nopayloaddb.db_router.ReadWriteRouter'] + REST_FRAMEWORK = { # 'DEFAULT_AUTHENTICATION_CLASSES': ( # 'rest_framework.authentication.TokenAuthentication',