0

Here is the case. A method or function has a couple of database queries. There are two databases, such as DATABASE_A and DATABASE_B.

def method():
    # database queries here
    orders = Order.objects.filter(...)
    products = Product.objects.filter(...)

How to achieve the follows easily with decorator or any other ways? Can anyone give an example? model.objects.using(some database) is a known way, however there are a lot of queries in the method. I don't want to touch them, so this is not an option.

# Here call method() using DATABASE_A
# here call method() using DATABASE_B
0

2 Answers 2

1

As specified in Multiple databases docs:

You can select the database for a QuerySet at any point in the QuerySet “chain.” Just call using() on the QuerySet to get another QuerySet that uses the specified database.

>>> # This will run on the 'default' database.
>>> Author.objects.all()

>>> # So will this.
>>> Author.objects.using('default').all()

>>> # This will run on the 'other' database.
>>> Author.objects.using('other').all()
Sign up to request clarification or add additional context in comments.

1 Comment

using(somedb) is a known way, however there are a lot of queries in the method. I don't want to touch them. So the way you given is not an option.
1

Django, Databases, and Decorators has a solution using django DATABASE_ROUTERS.

Here you can find an example from docs.djangoproject.com. The follows is the summary from 'Django, Databases, and Decorators':

1. Writing a custom decorator

Here name the decorator as decorators.py:

from functools import wraps

try:
    from threading import local
except ImportError:
    from _threading_local import local

threadlocal = local()


class use_db_for_reads(object):

    def __init__(self, database_name):
        self.database_name = database_name

    def __enter__(self):
        setattr(threadlocal, 'DB_FOR_READ_ONLY', self.database_name)

    def __exit__(self, exc_type, exc_value, traceback):
        setattr(threadlocal, 'DB_FOR_READ_ONLY', None)

    def __call__(self, test_func):
        @wraps(test_func)
        def inner(*args, **kwargs):
            return test_func(*args, **kwargs)
        return inner


def get_thread_local(attr, default=None):
    return getattr(threadlocal, attr, default)


class AnalyticsRouter(object):

    def db_for_read(self, model, **hints):
        return get_thread_local('DB_FOR_READ_ONLY', 'default')

    def db_for_write(self, model, **hints):
        return 'default'

    def allow_relation(self, obj1, obj2, **hints):
        return True

2. update settings

Here is an example of DATABASES in settings.py:

DATABASES = {
    'default': {
        ...
    },
    'read-only': {
        ...
    }
}

DATABASE_ROUTERS = ['decorators.AnalyticsRouter']

3. Here is how to use it

from decorators import use_db_for_reads

# Here call method() using DATABASE_A
with use_db_for_reads(DATABASE_A):
    method()

# here call method() using DATABASE_B
with use_db_for_reads(DATABASE_B):
    method()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.