Warning in AppConfig.ready() after Django update

Hi! Recently, I’ve updated Django to the latest version, 5.1.2, and since then, every time I start the server, I get this warning:

c:\projects\myproject\venv\Lib\site-packages\django\db\backends\utils.py:98: RuntimeWarning: Accessing the database during app initialization is discouraged. To fix this warning, avoid executing queries in AppConfig.ready() or when your app modules are imported.

From what I’ve searched so far, my apps.py file is causing this due to the operation it’s executing on the database:

from django.apps import AppConfig


class TenantsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'tenants'

    def ready(self):
        self.load_additional_databases()

    def load_additional_databases(self):
        from django.conf import settings

        from .models import DatabaseConfig

        for config in DatabaseConfig.objects.all():
            if config.name not in settings.DATABASES:

                db_settings = settings.DATABASES['default'].copy()

                db_settings.update({
                    'ENGINE': config.engine,
                    'NAME': config.database_name,
                    'USER': config.user,
                    'PASSWORD': config.password,
                    'HOST': config.host,
                    'PORT': config.port,
                })

                settings.DATABASES[config.name] = db_settings

My settings.py has two main databases (default and tenants) hard-coded and the other configurations should be updated with data from the model DatabaseConfig when I start the server. The problem is that I need this exact behavior, but the solution I’ve found so far, is to use connection_created which makes this run for every database query. This is the implementation:

def db_config(**kwargs):
    from django.conf import settings

    from .models import DatabaseConfig

    for config in DatabaseConfig.objects.all():
        if config.name not in settings.DATABASES:

            db_settings = settings.DATABASES['default'].copy()

            db_settings.update({
                'ENGINE': config.engine,
                'NAME': config.database_name,
                'USER': config.user,
                'PASSWORD': config.password,
                'HOST': config.host,
                'PORT': config.port,
            })

            settings.DATABASES[config.name] = db_settings


class TenantsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'tenants'

    def ready(self):
        connection_created.connect(db_config)

Is there another way to achieve the previous behavior in the ready method? Or is it really that bad to keep using it with the warning? For what I understand from the docs, this should be avoided, but is not necessarily bad. The docs mention the save() and delete() methods, but here I’m just updating the settings.py file. Any help would be appreciated. Thanks!

The warning in the docs go back to where I think the ready method was introduced Django 1.7. It’s only the warning message that is more recent, which was added for ticket 33143, with a reference to the discussion at https://groups.google.com/g/django-developers/c/7JwWatLfP44/
That discussion mentions some of the potential problems.
Whether those potential issues are something that concerns you is a question only you can answer.

We use a slightly different approach (it’s for a different situation, but the idea may be close enough).

In every view accessing a foreign database, there’s a variable identifying which database is being accessed. Before any queries are run in that view, the django.db.connections.databases dict (not settings) is checked to verify the database is defined in the connections, and if it isn’t, the settings are updated at that point

(In our case, the user may dynamically add a database while the system is up. The basic idea is the same - the connection information is written to a table and the connections are updated on an as-needed basis.)

In the Google Group discussion it’s pointed out that tests will execute whatever is in the ready method before running, and the docs clearly states that this is also applies to any management commands. Since I’m not making any major changes to the method, I don’t think it will be a problem here. Even if something comes up in the future, it’s easier to know where to look. Very helpful answer. Also, thank you for sharing your approach!