Allow a Django command to use a file or stdin / stdout
Here’s a little approach I’ve started using when writing Python scripts and Django console commands. It allows them to easily handle i/o from the commandline or from a file without much effort.
Rather than using sys.stdin
or sys.stdout
directly, you define an
argparse.FileType
argument for the command, and set the default to
sys.stdin
.
For example, in a Django command:
from sys import stdin, stdout
from argparse import FileType
from django.core.management.base import BaseCommand
class Command(BaseCommand):
def add_arguments(self, parser: ArgumentParser):
parser.add_argument('input', nargs='?', type=FileType('r'),
default=stdin)
parser.add_argument('output', nargs='?', type=FileType('w'),
default=stdout)
def handle(self, *args, **options):
input = options['input']
output = options['output']
# ...
Now the command is flexible as you can pipe data to or from it:
cat input-file.txt | ./manage.py foo_command > output-file.txt
Or just tell it the names of files to use:
./manage.py foo_command input-file.txt output-file.txt
This makes the command easier to use in a variety of situations. It also has the
benefit that when writing a test for a Django command, it’s easier to pass the
input data than if it was using sys.stdin
directly:
from io import StringIO
from django.core.management import call_command
from django.test import TestCase
class FooCommandTest(TestCase):
def test_foo_command(self):
out = StringIO()
input = 'path-to-some-file.txt'
call_command('foo_command', input, stdout=out)
# ...
This is a handy approach for testing commands that usually take their input from stdin.