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.py
file 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.log
file - 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.