[Fixed]-There is a way to add features to an existing django command?

20👍

Just realize that you can override the commands just easily as making an app with a command with the same name.

So I create an app and create a file with the same name as runserver, and later on that extend the runserver base class to add a new feature before it runs.

For example, I want to run the command $ compass watch, just before runserver starts and keep it running along runserver execution.

"""
Start $compass watch, command when you do $python manage.py runserver

file: main/management/commands/runserver.py

Add ´main´ app to the last of the installed apps
"""

from optparse import make_option
import os
import subprocess

from django.core.management.base import BaseCommand, CommandError
from django.core.management.commands.runserver import BaseRunserverCommand
from django.conf import settings

class Command(BaseRunserverCommand):
    option_list = BaseRunserverCommand.option_list + (
        make_option('--adminmedia', dest='admin_media_path', default='',
            help='Specifies the directory from which to serve admin media.'),
        make_option('--watch', dest='compass_project_path', default=settings.MEDIA_ROOT,
            help='Specifies the project directory for compass.'),
    )

    def inner_run(self, *args, **options):
        self.compass_project_path = options.get('compass_project_path', settings.MEDIA_ROOT)

        self.stdout.write("Starting the compass watch command for %r\n" % self.compass_project_path)
        self.compass_pid = subprocess.Popen(["compass watch %s" % self.compass_project_path],
            shell=True,
            stdin=subprocess.PIPE,
            stdout=self.stdout,
            stderr=self.stderr)
        self.stdout.write("Compas watch process on %r\n" % self.compass_pid.pid)

        super(Command, self).inner_run(*args, **options)

This works just fine.

Look at https://docs.djangoproject.com/en/dev/howto/custom-management-commands/ for more details about django commands

Hope someone find this helpful

7👍

To further expand on @Mario César’s excellent answer, I would like to provide a modern version of his initial 2011 code adapted for Django 1.8+:

Before Django 1.8, management commands were based on the optparse module […] Now that management commands use argparse for argument parsing, all arguments are passed in **options by default […]

Source

Additionally, I would like to point out that the specific command runserver that was chosen in the question has a slight complication making it both a good and bad example.

Bad example, because the complication is that the command is overridden by Django itself as well. Indeed, Django uses the same method as proposed by Mario: Django overrides it in the staticfiles app (see Django code on github) in order to offer the additional static files options.

Therefore, it is better to override the staticfiles app command rather than the core command, if one uses static. This answers to @ts_pati’s comment too on why there is problem. The Django code of staticfiles is the good example on how to override it, but this time importing the staticfiles in order not to lose that functionality:

from django.contrib.staticfiles.management.commands.runserver import Command as StaticfilesRunserverCommand


class Command(StaticfilesRunserverCommand):
    help = "Starts a lightweight Web server for development, serves static files and does some custom fancy stuff."

    def add_arguments(self, parser):
        super(Command, self).add_arguments(parser)
        parser.add_argument('--my-custom-argument', action="...", dest='my_custom_handler', default=True, help='do some stuff in fancy ways')

    def get_handler(self, *args, **options):
        """
        My fancy stuff function.
        """
        handler = super(Command, self).get_handler(*args, **options)
        my_custom_handler = options.get('my_custom_handler', True)
        # do stuff here
        return handler

EDIT: I would also like to add the order of this in INSTALLED_APPS is apparently important and it has to be before django.contrib.staticfiles.

👤Wtower

1👍

Write your own management command in your app that runs your command and then calls Django’s built-in implementation.

Leave a comment