Create a custom manage.py command in Django
Posted on Sun 01 March 2020 in Python • 3 min read
Table of Contents
If you ever used Django you have propbably used its manage.py to execute different things like:
python manage.py runserver
python manage.py createsuperuser
There are lots of useful commands available built-in that you can use, as can be seen in the documentation (django-admin and manage.py). For example:
- dumpdata - export data from an app to JSON or some other format
- loaddata - import data to the database
- migrate - sync the database with the current set of models and migrations
You can also build your own command for some administrative task you might have. For example if you want to have some scheduled maintenance done, you can build such a command and run it with some scheduled job. In this article I’ll show you how you can build your own custom manage.py command to fill data from an API request into your database.
The full source code for this sample Django app can be found at GitHub
Scenario
Let’s say that we for some reason want to fill a database model we have with some beer data. If you’re interested in beer, you might recall that Brewdog had a series of beers named "Hello, my name is...".
We will get information about those beers from a free API at https://punkapi.com
Django Model
In our Django app we build the model for the data:
# models.py
from django.db import models
class Beer(models.Model):
name = models.CharField(max_length=100)
tagline = models.CharField(max_length=200)
first_brewed = models.CharField(max_length=20)
description = models.TextField()
added = models.DateTimeField(auto_now_add=True)
edited = models.DateTimeField(auto_now=True)
def __str__(self):
return f'Beer: {self.name}'
We migrate the model into the database.
python manage.py makemigrations beers
python manage.py migrate
Building our custom command
To add a new command we need to create a folder structure under our apps folder in the project.
Quote from the Django documentation
To do this, add a management/commands directory to the application. Django will register a manage.py command for each Python module in that directory whose name doesn’t begin with an underscore
So in this case we create an update_beers.py
in the following folder:
beers/
__init__.py
models.py
management/
__init__.py
commands/
update_beers.py
The command code
The command code should include a Command class that sub-classes Djangos BaseCommand. The BaseCommand has one method that needs to be implemented, and that’s the handle
method. It will contain the logic of our command.
The basic structure of the code needed can be seen below.
from django.core.management import BaseCommand
from beers.models import Beer
class Command(BaseCommand):
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
help = "Update Beer table from punkapi.com"
def handle(self, *args, **options):
# Implement the logic here
pass
In this case, when we want to request an API for data that then will be written to the database, we will also use the requests library.
The full code for the command could look like this. (It is just for demo purposes. In a real world scenario we would probably not use the name of the beer to query if the beer already exists in the database.)
from django.core.management import BaseCommand
import requests
from beers.models import Beer
class Command(BaseCommand):
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
help = "Update Beer table from punkapi.com"
def handle(self, *args, **options):
# Request data from the beer API
response = requests.get('https://api.punkapi.com/v2/beers/?beer_name=my_name_is')
# Loop through the response
for beer in response.json():
try:
beer_row = Beer.objects.get(name=beer['name'])
except Beer.DoesNotExist:
beer_row = None
if beer_row:
# Beer already exists - do nothing
self.stdout.write(f'{beer_row.name} already exists.')
continue
else:
# Add beer to db
self.stdout.write('Create new row')
beer_row = Beer()
beer_row.name = beer['name']
beer_row.tagline = beer['tagline']
beer_row.first_brewed = beer['first_brewed']
beer_row.description = beer['description']
beer_row.save()
self.stdout.write('#########################')
self.stdout.write('Updated Beer list')
self.stdout.write('#########################')
Execute our custom command
Execute our custom command by running:
python manage.py update_beers
Which will show the output:
If all goes well, the API will be requested for data about the beers, and the data will be written to the database.
Conclusion
In this article I have demonstrated how you can build your own custom manage.py command in Django. The article shows how you can build a custom command to get data from an API via the requests
library and store that in the database.
Resources
- Writing custom django-admin commands (Django documentation)
- The full source code of this Beer app at GitHub
Let me know on Twitter if I can improve this article, or if you have other resources to help out with understanding this topic.