Django

Django Management Commands via Cron

You want to periodically run a Django management command. How do you go about it?

Let’s get to it.

Folder Structure

app_folder > management > commands > our_command_name.pyYou have a Django project, say, MyProject. You have an app called, main. Create in this main folder a management folder. Remember to add an __init__.py in this management folder.

Add a commands folder within the management folder, and include an __init__.py in this folder too.

You only need to include the __init__.py things when using Python less than version 3.3

Our Problem

Let’s say we want to delete items created some 3 or 6 months past automatically. One way is to create a management command, which would be run, say daily, clearing these objects for us.

The Solution

In the above, you see the cleanpackages.pyfile in the commands folder?

The name you give to the file within this commands folder becomes the command you would invoke on the command line.

So in our above example, we would invoke it as python manage.py cleanpackages 3month

Here’s what our cleanpackages.py file might contain:

I am not doing anything supernatural here. The details are within this doc page: http://devdocs.io/django~1.11/howto/custom-management-commands

from datetime import datetime, timedelta
from django.utils import timezone
from django.core.management.base import BaseCommand, CommandError
from main.models import Package

class Command(BaseCommand):
  help = 'Clears packages older than 90 days from today'

  def add_arguments(self, parser):
    parser.add_argument('duration')

  def handle(self, *args, **options):
    if options['duration'] == 'month':
      number_of_days = 30
    elif options['duration'] == '3month':
      number_of_days = 90
    elif options['duration'] == '6month':
      number_of_days = 180
    else:
      number_of_days = 30

    # self.stdout.write(self.style.SUCCESS('Number of days to delete "%s"' % number_of_days))

    today = timezone.now()
    past_date = today - timedelta(days=number_of_days);

    # this ensures we don't bother running through already marked true
    # objects as deleted.
    to_delete = Package.objects.filter(is_deleted=False, createdAt__lte=past_date)
    
    for item in to_delete:
      item.deleted_at = timezone.now()
      item.is_deleted = True # this assumes you're doing a soft delete on your model
      item.save()

      self.stdout.write(self.style.SUCCESS('Removed "%s"' % to_delete))

Not sure if worth the time to explain what is happening above because the code reads like English already. All Hail Python. You could always leave a question in the comment section below if anything isn’t clear. Happy to help.

Now let’s run the command via cron.

Cron-ify our Command

Please, if you use cron or have used before, yet don’t know of crontab.guru, then happy to introduce to you.

Don’t get stuck playing around on the site. Come back and let’s continue. However I think it is such a cool website for cron newbies like myself.

To make our cron command easier to read, let’s create a bash file to house our reference to the django management command. Let’s call it cleanpackages.sh

#!/bin/bash
cd /home/your-username/DjangoProject
source ./.env/bin/activate
python manage.py cleanpackages 3month

Now the ./.env/bin/activate assumes that’s where your virtual environment folder is. If you’re using virtualenvwrapper or so, it might be in your virtualenvs folder somewhere. Point to your virtual environment appropriately.

The above commands are nothing new. They are just what we would usually enter into the command line, only this time, they’re packaged into a bash file.

In English:

  • Change directory to our django project folder
  • activate the virtual environment
  • then run the management command just like you would

Does it get any simpler than this?

Make this cleanpackages.sh executable via

chmod +x cleanpackages.sh

You can test if your command is right, by simply running a ./cleanpackages.sh

If all runs fine, then you’re good to go.

Then to our cron.

$ crontab -e

Then let’s add this:

@daily /home/your-username/cleanpackages.sh >> /home/your-username/logs/cleanpackages.log 2>&1 && curl -sm 30 k.wdt.io/<your-email>/<cron-name>?c=@daily

In English, the above is saying:

  • Everyday, at 00:00 midnight,
  • cleanpackages.sh will be run.
  • The logs of whatever happened (what you would see in the terminal console) would be pushed into the cleanpackages.logfile
  • THEN, the beauty of crontab.guru comes in. We make a curl request to an endpoint, which if it works or not, we get an email (or so). No idea if it works or not. Never happened.

On that note, our management command will run every day.

Conclusion

I hope you enjoyed this one. Hope to see you in the next one.

Any questions, don’t hesitate to leave them in the comments.

 

Related Articles

Check Also
Close
Back to top button