Skip to content

Commit 5043f15

Browse files
committed
feat(refactor): add if condition to check wether database parameter is str along with initial else condition that throws an error specifying what the database parameter should be of type. add comments and clean up
1 parent cd8d593 commit 5043f15

File tree

1 file changed

+35
-72
lines changed

1 file changed

+35
-72
lines changed

dynamic_db_router/router.py

Lines changed: 35 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,82 @@
1-
import threading
1+
from contextvars import ContextVar
22
from functools import wraps
33
from uuid import uuid4
4-
from contextvars import ContextVar
5-
64
from django.db import connections
75

8-
THREAD_LOCAL = threading.local()
9-
6+
# Define context variables for read and write database settings
7+
# These variables will maintain database preferences per context
108
DB_FOR_READ_OVERRIDE = ContextVar('DB_FOR_READ_OVERRIDE', default='default')
119
DB_FOR_WRITE_OVERRIDE = ContextVar('DB_FOR_WRITE_OVERRIDE', default='default')
12-
1310

14-
class DynamicDbRouter(object):
15-
"""A router that decides what db to read from based on a variable
16-
local to the current thread.
11+
12+
class DynamicDbRouter:
13+
"""
14+
A router that dynamically determines which database to perform read and write operations
15+
on based on the current execution context. It supports both synchronous and asynchronous code.
1716
"""
18-
17+
1918
def db_for_read(self, model, **hints):
2019
return DB_FOR_READ_OVERRIDE.get()
21-
# return getattr(THREAD_LOCAL, 'DB_FOR_READ_OVERRIDE', ['default'])[-1]
22-
20+
2321
def db_for_write(self, model, **hints):
2422
return DB_FOR_WRITE_OVERRIDE.get()
25-
# return getattr(THREAD_LOCAL, 'DB_FOR_WRITE_OVERRIDE', ['default'])[-1]
26-
23+
2724
def allow_relation(self, *args, **kwargs):
2825
return True
29-
26+
3027
def allow_syncdb(self, *args, **kwargs):
3128
return None
32-
29+
3330
def allow_migrate(self, *args, **kwargs):
3431
return None
3532

36-
37-
class in_database(object):
38-
"""A decorator and context manager to do queries on a given database.
39-
40-
:type database: str or dict
41-
:param database: The database to run queries on. A string
42-
will route through the matching database in
43-
``django.conf.settings.DATABASES``. A dictionary will set up a
44-
connection with the given configuration and route queries to it.
45-
46-
:type read: bool, optional
47-
:param read: Controls whether database reads will route through
48-
the provided database. If ``False``, reads will route through
49-
the ``'default'`` database. Defaults to ``True``.
50-
51-
:type write: bool, optional
52-
:param write: Controls whether database writes will route to
53-
the provided database. If ``False``, writes will route to
54-
the ``'default'`` database. Defaults to ``False``.
55-
56-
When used as eithe a decorator or a context manager, `in_database`
57-
requires a single argument, which is the name of the database to
58-
route queries to, or a configuration dictionary for a database to
59-
route to.
60-
61-
Usage as a context manager:
62-
63-
.. code-block:: python
64-
65-
from my_django_app.utils import tricky_query
66-
67-
with in_database('Database_A'):
68-
results = tricky_query()
69-
70-
Usage as a decorator:
71-
72-
.. code-block:: python
73-
74-
from my_django_app.models import Account
75-
76-
@in_database('Database_B')
77-
def lowest_id_account():
78-
Account.objects.order_by('-id')[0]
79-
80-
Used with a configuration dictionary:
81-
82-
.. code-block:: python
83-
84-
db_config = {'ENGINE': 'django.db.backends.sqlite3',
85-
'NAME': 'path/to/mydatabase.db'}
86-
with in_database(db_config):
87-
# Run queries
33+
class in_database:
34+
"""
35+
A context manager and decorator for setting a specific database for the duration of a block of code.
8836
"""
8937
def __init__(self, database, read=True, write=False):
9038
self.read = read
9139
self.write = write
9240
self.database = database
9341
self.created_db_config = False
94-
if isinstance(database, dict):
42+
43+
# Handle database parameter either as a string (alias) or as a dict (configuration)
44+
if isinstance(database, str):
45+
self.database = database
46+
elif isinstance(database, dict):
47+
# If it's a dict, create a unique database configuration
9548
self.created_db_config = True
9649
self.unique_db_id = str(uuid4())
9750
connections.databases[self.unique_db_id] = database
9851
self.database = self.unique_db_id
99-
52+
else:
53+
raise ValueError("database must be an identifier (str) for an existing db, "
54+
"or a complete configuration (dict).")
55+
10056
def __enter__(self):
57+
# Capture the current database settings
10158
self.original_read_db = DB_FOR_READ_OVERRIDE.get()
10259
self.original_write_db = DB_FOR_WRITE_OVERRIDE.get()
60+
61+
# Override the database settings for the duration of the context
10362
if self.read:
10463
DB_FOR_READ_OVERRIDE.set(self.database)
10564
if self.write:
10665
DB_FOR_WRITE_OVERRIDE.set(self.database)
10766
return self
108-
67+
10968
def __exit__(self, exc_type, exc_value, traceback):
69+
# Restore the original database settings after the context.
11070
DB_FOR_READ_OVERRIDE.set(self.original_read_db)
11171
DB_FOR_WRITE_OVERRIDE.set(self.original_write_db)
72+
73+
# Close and delete created database configuration
11274
if self.created_db_config:
11375
connections[self.unique_db_id].close()
11476
del connections.databases[self.unique_db_id]
115-
77+
11678
def __call__(self, querying_func):
79+
# Allow the object to be used as a decorator
11780
@wraps(querying_func)
11881
def inner(*args, **kwargs):
11982
with self:

0 commit comments

Comments
 (0)