# RunScript

synopsis:

  Runs a script in the Django context.

## Introduction

The runscript command lets you run an arbitrary set of python commands within the Django context. It offers the same usability and functionality as running a set of commands in shell accessed by:

    $ python manage.py shell

## Getting Started

This example assumes you have followed the tutorial for Django 1.8+, and created a polls app containing a `Question` model. We will create a script that deletes all of the questions from the database.

To get started create a scripts directory in your project root, next to manage.py:

    $ mkdir scripts
    $ touch scripts/__init__.py

Note: The __init__.py file is necessary so that the folder is picked up as a python package.

Next, create a python file with the name of the script you want to run within the scripts directory:

    $ touch scripts/delete_all_questions.py

This file must implement a run() function. This is what gets called when you run the script. You can import any models or other parts of your django project to use in these scripts.

For example:

    # scripts/delete_all_questions.py

    from polls.models import Question

    def run():
        # Fetch all questions
        questions = Question.objects.all()
        # Delete questions
        questions.delete()

Note: You can put a script inside a scripts folder in any of your apps too.

## Usage

To run any script you use the command runscript with the name of the script that you want to run.

For example:

    $ python manage.py runscript delete_all_questions

Note: The command first checks for scripts in your apps i.e. app_name/scripts folder and runs them before checking for and running scripts in the project_root/scripts folder. You can have multiple scripts with the same name and they will all be run sequentially.

## Passing arguments

You can pass arguments from the command line to your script by passing a space separated list of values with `--script-args`. For example:

    $ python manage.py runscript delete_all_questions --script-args staleonly

The list of argument values gets passed as arguments to your run() function. For example:

    # scripts/delete_all_questions.py
    from datetime import timedelta

    from django.utils import timezone

    from polls.models import Question

    def run(*args):
        # Get all questions
        questions = Question.objects.all()
        if 'staleonly' in args:
            # Only get questions more than 100 days old
            questions = questions.filter(pub_date__lt=timezone.now() - timedelta(days=100))
        # Delete questions
        questions.delete()

## Setting execution directory

You can set scripts execution directory using `--chdir` option or `settings.RUNSCRIPT_CHDIR`. You can also set scripts execution directory policy using `--dir-policy` option or `settings.RUNSCRIPT_CHDIR_POLICY`.

It can be one of the following:

- none - start all scripts in current directory.

- each - start all scripts in their directories.

- root - start all scripts in `BASE_DIR` directory.

Assume this simplified directory structure:

    django_project_dir/
    ├-first_app/
    │ └-scripts/
    │   ├-first_script.py
    ├-second_app/
    │ └-scripts/
    │   ├-second_script.py
    ├-manage.py
    ├-other_folder/
    │ └-some_file.py

Assume you are in `other_folder` directory. You can set execution directory for both scripts using this command:

    $ python ../manage.py runscript first_script second_script --chdir /django_project_dir/second_app
    # scripts will be executed from second_app directory

You can run both scripts with `NONE` policy using this command:

    $ python ../manage.py runscript first_script second_script --dir-policy none
      # scripts will be executed from other_folder directory

You can run both scripts with `EACH` policy using this command:

    $ python ../manage.py runscript first_script second_script --dir-policy each
      # first_script will be executed from first_app and second script will be executed from second_app

You can run both scripts with `ROOT` policy using this command:

    $ python ../manage.py runscript first_script second_script --dir-policy root
      # scripts will be executed from django_project_dir directory

## Errors and exit codes

If an exception is encountered the execution of the scripts will stop, a traceback is shown and the command will return an exit code.

To control the exit-code you can either use CommandError(“something went terribly wrong”, returncode=123) in your script or has the run(…) function return the exit_code. Where any exit code other then 0 will indicate failure, just like regular shell commands.

This means you can use runscript in your CI/CD pipelines or other automated scripts and it should behave like any other shell command.

## Continue on errors

If you want runscript to continue running scripts even if errors occurs you can set -c:

    $ python manage.py runscript delete_all_questions another_script --continue-on-error

This will continue running ‘another_script’ even if an exception was raised or exit code was returned in ‘delete_all_questions’.

When all the scripts has been run runscript will exit with the last non-zero exit code.

Note: It is possible to do raise CommandError(…, returncode=0) which will lead to an exception with exit code 0.

## Debugging

If an exception occurs you will get a traceback by default. You can use CommandError in the same way as with other custom management commands.

To get a traceback from a CommandError specify `--traceback`. For example:

    $ python manage.py runscript delete_all_questions --traceback

If you do not want to see tracebacks at all you can specify:

    $ python manage.py runscript delete_all_questions --no-traceback