3

We always run our tests off of settings_test, like so: ./manage.py test someapp --settings=settings_test. The problem is that it's sometimes problematic to remember to add the option.

I'd like to introduce a common app that just has the management command test.py. Depending on it's placement in the INSTALLED_APPS setting, it will override the default. Inside the command itself, I'd like to change the default of the --settings option. How can I do that?

I am aware that I can create a local.py file that is similar to manage.py but with settings_test instead of settings. However, the point is to still run ./manage.py, but with having settings_test as default instead of constantly typing "--settings=settings_test" after ./manage.py test someapp

Sab'
  • 31
  • 1
  • 2
  • Did you consider just writing an alias for `manage.py test someapp --settings=settings_test` in your shell? – Pavel Anossov Mar 11 '13 at 20:47
  • It's just for practical purposes: creating an alias means that every programmer who is working on the project will have to create the alias on their side and I want to avoid that. I'd like to set the settings_test as default so the other programmers who generally forget to add the option won't end up focusing on the wrong type of errors that wouldn't appear if they had added it. – Sab' Mar 11 '13 at 22:02
  • Why not create a Makefile with commonly used commands like this and check it in with your repository? Then you can just `make test_someapp` and be done with it. – mVChr Mar 11 '13 at 22:05
  • @mVChr: we would like to still use the ./manage.py test someapp for convention and simplicity. Is it possible to do that with Makefile? – Sab' Mar 11 '13 at 22:16
  • Yes, your Makefile command would look like: `test_someapp:\n python ./manage.py test someapp --settings=settings_test` – mVChr Mar 11 '13 at 22:31
  • I am totally unfamiliar with makefile, can you show how you create one and how you run it? – Sab' Mar 11 '13 at 23:55

3 Answers3

3

After several attempts, I found that manage.py sets the settings long enough before the actual management command gets called that it's basically impossible (as far as I can tell) to change them there. Eventually I decided that, since it's seems to be OK to alter manage.py - there's your default settings file there, for instance - that would be a reasonable place to deal with this, so I implemented it there:

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings")

    argv = sys.argv
    try:
        if argv[1] == 'test' and not any([k.startswith('--settings') for k in argv]):
            os.environ["DJANGO_SETTINGS_MODULE"] = "myapp.test_settings"
    except IndexError:
        pass

    from django.core.management import execute_from_command_line
    execute_from_command_line(argv)

This seems to work as expected and I don't see any major disadvantage or problem - happy to be corrected.

pgcd
  • 538
  • 6
  • 13
2

You can override default commands by implemeting them in your app (say: common_app).

Create a management.commands package in common_app. And implement a test command in there:

# this is commonapp/management/commands/test.py
try:
    from south.management.commands import test
except ImportError:
    from django.core.management.commands import test


class Command(test.Command):
    def handle(self, *args, **kwargs):
        kwargs['settings'] = kwargs.get('settings', 'common_app.settings')
        super(Command, self).handle(*args, **kwargs)

This will use the settings.py file in common_app if there's no '--settings=' flag in given. But you can change that string in "settings_test" and it will load the first settings_test.py it can find on your python path.

South does this command overriding too, so for completeness I added the south import. But if any of your other installed apps also override the test command... you get the idea.

Don't forget the empty __init__.py files to make 'management' and 'commands' packages.

RTFM for more info on Django custom management commands.

Chris Wesseling
  • 6,226
  • 2
  • 36
  • 72
  • that code doesn't really work as expected - at least in Django 1.7. The line `kwargs['settings'] = kwargs.get('settings', 'common_app.settings')` seems to never actually change the value, because .get() finds the key, which happens to have a value of None. I have made it work by changing it to: `kwargs['settings'] = kwargs.get('settings', None) or 'common_app.settings'` HTH. – pgcd May 22 '15 at 07:44
  • There's actually another problem too: apparently, it looks like Django 1.7 has already parsed the settings when it gets to handle() -- I just tried setting kwargs['settings'] to random gibberish and it plodded on happily. – pgcd May 22 '15 at 07:55
1

You can override a django command as @Chris Wesseling describe in his answer.

Note: if there are other apps that are extending the command, move your app above those apps in the INSTALLED_APPS list, i.e.

  • to override runserver command and you are using 'django.contrib.staticfiles', place your app before staticfiles app
  • to override test command and you are using a test app i.e. django_nose, the same, place your app before django_nose app
sergiuz
  • 5,353
  • 1
  • 35
  • 51