Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: running code on every startup but after database is migrated

Tags:

python

django

I thought there was an easy answer to this in recent versions of Django but I can't find it.

I have code that touches the database. I want it to run every time Django starts up. I seem to have two options:

Option 1. AppConfig.ready() - this works but also runs before database tables have been created (i.e. during tests or when reinitializing the app without data). If I use this I have to catch multiple types of Exceptions and guess that the cause is an empty db:

def is_db_init_error(e, table_name):
    return ("{}' doesn't exist".format(table_name) in str(e) or
            "no such table: {}".format(table_name) in str(e)
    )

try:
    # doing stuff 
except Exception as e:
    if not is_db_init_error(e, 'foo'):
        raise
    else:
        logger.warn("Skipping updating Foo object as db table doesn't exist")

Option 2. Use post_migrate.connect(foo_init, sender=self) - but this only runs when I do a migration.

Option 3. The old way - call it from urls.py - I wanted to keep stuff like this out of urls.py and I thought AppConfig was the one true path

I've settled for option 2 so far - I don't like the smelly try/except stuff in Option 1 and Option 3 bugs me as urls.py becomes a dumping ground.

However Option 2 often trips me up when I'm developing locally - I need to remember to run migrations whenever I want my init code to run. Things like pulling down a production db or similar often cause problems because migrations aren't triggered.

like image 227
Andy Baker Avatar asked Apr 13 '17 13:04

Andy Baker


People also ask

How does Django keep track of migrations?

Django keeps track of applied migrations in the Django migrations table. Django migrations consist of plain Python files containing a Migration class. Django knows which changes to perform from the operations list in the Migration classes. Django compares your models to a project state it builds from the migrations.

What is difference between migrate and migration in Django?

You should think of migrations as a version control system for your database schema. makemigrations is responsible for packaging up your model changes into individual migration files - analogous to commits - and migrate is responsible for applying those to your database.

Can you undo a migration Django?

The gist is django can only rollback migrations if the files are present in the location. If you do not want to rollback a migration path (i.e A here), remove it from the project location while performing rollback.


1 Answers

I would suggest the connection_created signal, which is:

Sent when the database wrapper makes the initial connection to the database. This is particularly useful if you’d like to send any post connection commands to the SQL backend.

So it will execute the signal's code when the app connects to the database at the start of the application's cycle.

It will also work within a multiple database configuration and even separate the connections made by the app at initialization:

connection
The database connection that was opened. This can be used in a multiple-database configuration to differentiate connection signals from different databases.


Note:
You may want to consider using a combination of post_migrate and connection_created signals while checking inside your AppConfig.ready() if a migration happened (ex. flag the activation of a post_migrate signal):

from django.apps import AppConfig
from django.db.models.signals import post_migrate, connection_created
# OR for Django 2.0+
# django.db.backends.signals import post_migrate, connection_created

migration_happened = false

def post_migration_callback(sender, **kwargs):
    ...
    migration_happened = true


def init_my_app(sender, connection):
    ...


class MyAppConfig(AppConfig):
    ...

    def ready(self):
        post_migrate.connect(post_migration_callback, sender=self)

        if !migration_happened:
            connection_created.connect(init_my_app, sender=self)
like image 96
John Moutafis Avatar answered Sep 21 '22 12:09

John Moutafis