How to add database routers to a Django project
Asked Answered
P

3

9

I'm following the instructions on how to handle multiple databases within one Django project from here topics/db/multi-db

I've created the two routers required. They are saved as ./database_routers/discourse.py and ./database_routers/wordpress.py

The contents of ./database_routers/discourse.py is

class DiscourseRouter:
    """
    A router to control all database operations on models in the
    discourse application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read discourse models go to discourse.
        """
        if model._meta.app_label == 'discourse':
            return 'discourse'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write discourse models go to discourse.
        """
        if model._meta.app_label == 'discourse':
            return 'discourse'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the discourse app is involved.
        """
        if obj1._meta.app_label == 'discourse' or \
           obj2._meta.app_label == 'discourse':
           return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Make sure the discourse app only appears in the 'discourse'
        database.
        """
        if app_label == 'discourse':
            return db == 'discourse'
        return None

The contents of ./database_routers/wordpress.py is

class WordpressRouter:
    """
    A router to control all database operations on models in the
    wordpress application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read wordpress models go to wordpress.
        """
        if model._meta.app_label == 'wordpress':
            return 'wordpress'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write wordpress models go to wordpress.
        """
        if model._meta.app_label == 'wordpress':
            return 'wordpress'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the wordpress app is involved.
        """
        if obj1._meta.app_label == 'wordpress' or \
           obj2._meta.app_label == 'wordpress':
           return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Make sure the wordpress app only appears in the 'wordpress'
        database.
        """
        if app_label == 'wordpress':
            return db == 'wordpress'
        return None

I created an empty ./database_routers/__init__.py file

The database router settings in api/settings I've set to

DATABASE_ROUTERS = ['database_routers.DiscourseRouter', 'database_routers.WordpressRouter']

When I attempt to look at the project using shell plus I with

 ./manage.py shell_plus

I get

ImportError: Module "database_routers" does not define a "DiscourseRouter" attribute/class

How do you add database routers to a Django project such that python recognises the path directory_name.ClassName?

Parenthesis answered 19/12, 2018 at 21:49 Comment(13)
The title is very vague. Do you mind changing it?Ligan
Changed to How to add database routers to a Django projectParenthesis
@KeithJohnHutchison can you add database_routers module /class in problem statement.Pangolin
You have to create __init__.py file in your database_routers directory, not init.py.Burma
Added contents of database routers files.Parenthesis
I've got __init__.py file in database_routers directory, it is displaying as init.py for some reason.Parenthesis
@KeithJohnHutchison source use DATABASE_ROUTERS=[database_routers.db_for_read(DiscourseRouter),database_routers.db_for_read(WordpressRouter)] change thisPangolin
Ok, your path is wrong. Change 'database_routers.DiscourseRouter' to 'database_routers.discourse.DiscourseRouter'. Same for the other router.Burma
ImportError: Module "database_routers" does not define a "db_for_read(DiscourseRouter)" attribute/classParenthesis
@Burma Cheers and thanks. That works. I would not have guessed that :-)Parenthesis
That was pretty much the last option. ImportError is raised either because the file/class doesn't exist, __init__.py is missing or import path is wrong. :)Burma
@Burma Cool. How did you infer the namespace? I looked through the apps to see how the namespace was being specified and found nothing so I thought it was just the folder name that determined the namespace.Parenthesis
@Burma If you post an answer I'll mark it as the accepted solution.Parenthesis
D
10

You've missed out the module names.

DATABASE_ROUTERS = [
    'database_routers.discourse.DiscourseRouter', 
    'database_routers.wordpress.WordpressRouter'
]
Danby answered 19/12, 2018 at 22:33 Comment(0)
B
4

If I understand you very well, you intend to configure multiple databases right? Please find below:

class ExampleDatabaseRouter(object):
    """
    Determine how to route database calls for an app's models (in this case, for an app named Example).
    All other models will be routed to the next router in the DATABASE_ROUTERS setting if applicable,
    or otherwise to the default database.
    """

    def db_for_read(self, model, **hints):
        """Send all read operations on Example app models to `example_db`."""
        if model._meta.app_label == 'example':
            return 'example_db'
        return None

    def db_for_write(self, model, **hints):
        """Send all write operations on Example app models to `example_db`."""
        if model._meta.app_label == 'example':
            return 'example_db'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """Determine if relationship is allowed between two objects."""

        # Allow any relation between two models that are both in the Example app.
        if obj1._meta.app_label == 'example' and obj2._meta.app_label == 'example':
            return True
        # No opinion if neither object is in the Example app (defer to default or other routers).
        elif 'example' not in [obj1._meta.app_label, obj2._meta.app_label]:
            return None

        # Block relationship if one object is in the Example app and the other isn't.
            return False

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """Ensure that the Example app's models get created on the right database."""
        if app_label == 'example':
            # The Example app should be migrated only on the example_db database.
            return db == 'example_db'
        elif db == 'example_db':
            # Ensure that all other apps don't get migrated on the example_db database.
            return False

        # No opinion for all other scenarios
        return None

You can as well read more from enter link description here

Bianca answered 19/12, 2018 at 21:53 Comment(3)
I already have the router classes. The issue is how to get Django to recognise them from the settings file.Parenthesis
something like this? class User(models.Model): username = models.Charfield(ax_length=100) . . . class Meta: app_label = 'user_data' class Customer(models.Model): name = models.TextField(max_length=100) . . . class Meta: app_label = 'customer_data' . $ ./manage.py migrate --database=users_dbBianca
The issue was I missed the module names as per the answer from Daniel Roseman and the comments from Borut.Parenthesis
H
1

In addition to Daniel's answer, another option that gives you more control is to declare your classes in the __init__.

Contents of database_routers.__init__.py:

from .discourse.DiscourseRouter
from .wordpress.WordpressRouter


__all__ = [
    "DiscourseRouter",
    "WordpressRouter",
]

Then this allows you to remove the module names in settings.py:

DATABASE_ROUTERS = [
    'database_routers.DiscourseRouter', 
    'database_routers.WordpressRouter'
]

In other words, the original code would work.

This would gives you better control of which modules are public.

NB: This __init__.py / __all__ pattern is used broadly, eg: https://github.com/django/django/blob/master/django/db/init.py

Harelda answered 18/2, 2021 at 20:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.