DanH
DanH

Reputation: 5818

Detect whether code is being run in the context of migrate/makemigrations command

I have a model with dynamic choices, and I would like to return an empty choice list if I can guarantee that the code is being run in the event of a django-admin.py migrate / makemigrations command to prevent it either creating or warning about useless choice changes.

Code:

from artist.models import Performance
from location.models import Location

def lazy_discover_foreign_id_choices():
    choices = []

    performances = Performance.objects.all()
    choices += {performance.id: str(performance) for performance in performances}.items()

    locations = Location.objects.all()
    choices += {location.id: str(location) for location in locations}.items()

    return choices
lazy_discover_foreign_id_choices = lazy(lazy_discover_foreign_id_choices, list)


class DiscoverEntry(Model):
    foreign_id = models.PositiveIntegerField('Foreign Reference', choices=lazy_discover_foreign_id_choices(), )

So I would think if I can detect the run context in lazy_discover_foreign_id_choices then I can choose to output an empty choice list. I was thinking about testing sys.argv and __main__.__name__ but I'm hoping there's possibly a more reliable way or an API?

Upvotes: 10

Views: 2242

Answers (3)

ajaest
ajaest

Reputation: 627

Using the django.db.models.signals.pre_migrate should be enough to detect the migrate command. The drawback is that you cannot use it at configuration stage.

Upvotes: 0

Arctelix
Arctelix

Reputation: 4576

Here is a fairly non hacky way to do this (since django already creates flags for us) :

import sys
def lazy_discover_foreign_id_choices():
    if ('makemigrations' in sys.argv or 'migrate' in sys.argv):
        return []
    # Leave the rest as is.

This should work for all cases.

Upvotes: 21

aumo
aumo

Reputation: 5554

A solution I can think of would be to subclass the Django makemigrations command to set a flag before actually performing the actual operation.

Example:

Put that code in <someapp>/management/commands/makemigrations.py, it will override Django's default makemigrations command.

from django.core.management.commands import makemigrations
from django.db import migrations


class Command(makemigrations.Command):
    def handle(self, *args, **kwargs):
        # Set the flag.
        migrations.MIGRATION_OPERATION_IN_PROGRESS = True

        # Execute the normal behaviour.
        super(Command, self).handle(*args, **kwargs)

Do the same for the migrate command.

And modify your dynamic choices function:

from django.db import migrations


def lazy_discover_foreign_id_choices():
    if getattr(migrations, 'MIGRATION_OPERATION_IN_PROGRESS', False):
        return []
    # Leave the rest as is.

It is very hacky but fairly easy to setup.

Upvotes: 6

Related Questions