Today in this tutorial, we’re going to cover the following:
- How to start a project in Django.
- How to deliver JSON to a requester from our new API.
- How to deploy our new Django API to Heroku.
- How to include our new API in the RapidAPI marketplace.
Start a Django project
First, we’re going to create a new Django project named rapid-api-practice. Then, within that project, we will create a new app called api. Although this may seem odd at first, the “Django way” is to house an app, or more than likely multiple apps, within a single “project.”
We’ll put the whole thing in a root directory with the project’s name from the command line, like so:
mkdir rapid-api-practice cd rapid-api-practice
NOTE: It is highly recommended, but not required, that you create a virtual env within your root directory.
Then, we’ll install Django (order matters if using a venv):
pip3 install django
And finally, we can set up our new project with our single application, api. Notice the . in the command, which is telling Django to place the project in your current working directory.
django-admin startproject rapidapipractice .
Next, from within the newly created rapidapipractice project directory, run:
django-admin startapp api
That should leave you with a file tree that should now look something like this:
Next, we can set up our database by running our first migration:
python3 manage.py makemigrations python3 manage.py migrate
Finally, we’ll need to create our first user. Let’s call this user admin, and set the user’s password to password. From the terminal, run:
python3 manage.py createsuperuser Username: admin Email address: admin@fakeemail.com Password: ******** Password (again): ******** This password is too common. Bypass password validation and create user anyway? [y/N]: y Superuser created successfully.
Build out our API
Alright, now that our housekeeping is done, let’s move onto writing some of our own code. For this example, we’re just going to use Django’s built-in User and Group models, which will allow us to demonstrate converting SQL records into browser-friendly JSON without too much trouble.
Further, to speed and ease development, we’re going to use the Django Rest Framework module. To install, simply run:
pip3 install djangorestframework
Serializers (serializers.py)
First up we’re going to define some serializers, which will take care of the SQL to JSON conversion that we are looking for. Django Rest Framework can also handle other serializations like XML, and so forth, but we’re going to use JSON. If you’ve used Marshmallow for serialization in Flask, this will look familiar. Let’s create a new module within our api directory called serializers.py that we’ll use for our data representations.
From the command line:
cd rapidapipractice/api && touch serializers.py
Then, within the body of our new serializers.py file, type the following:
from django.contrib.auth.models import User, Group from rest_framework import serializers class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User fields = ['url', 'username', 'email', 'groups'] class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group fields = ['url', 'name']
Although Django Rest Framework offers a bunch of different relationship types, we’re using hyperlinked relations in this case by configuring Django Rest Framework’s serializers to extend HyperlinkedModelSerializer. This choice affords us the following:
- By default, it does not include an id field.
- It does include a clickable url field, which will execute the subsequent request when clicked.
Views (views.py)
Now, we’re going to define our views (or, more specifically, viewsets) in order to send our data from our backend to the browser. Open api/views.py and insert the following:
from django.contrib.auth.models import User, Group from rest_framework import viewsets From .serializers import UserSerializer, GroupSerializer class UserViewSet(viewsets.ModelViewSet): """ API endpoint that allows users to be viewed or edited. """ queryset = User.objects.all().order_by('-date_joined') serializer_class = UserSerializer class GroupViewSet(viewsets.ModelViewSet): """ API endpoint that allows groups to be viewed or edited. """ queryset = Group.objects.all() serializer_class = GroupSerializer
So, for standard CRUD operations on a SQL database, Django Rest Framework gives us these viewsets, which accept and handle GET, POST, PUT and DELETE requests. They also allow for a single endpoint to handle requests for list views of objects in the database, as well as individual object instances. Pretty sweet, really.
If for some reason we should need more granular control over the data that our API returns given a particular request, we can break out our viewsets into pure views, but in the case that viewsets will satisfy the needs of the use-case, it is the preferred means.
URLs (urls.py)
Okay, now we need to tell Django what views to return given a particular route. To do so, we’ll import include and path from the django.urls module, as well as routers from Django Rest Framework and, of course, the views that are to be returned. We can accomplish all of this by including the following code in our urls.py file.
from django.urls import include, path from rest_framework import routers from rapidapipractice.api import views router = routers.DefaultRouter() router.register(r'users', views.UserViewSet) router.register(r'groups', views.GroupViewSet) # Setup automatic URL routing # Additionally, we include login URLs for the browsable API. urlpatterns = [ path('', include(router.urls)), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')) ]
Because we’re using viewsets instead of views, we can automatically generate the URL conf for our API, by registering the viewsets with our router class.
Finally, we’re including default login and logout views for use with the browsable API. That’s optional, but useful if your API requires authentication and you want to use the browsable API.
Pagination (settings.py)
Pagination allows you to control how many objects per page are returned. To enable it, add the following lines to rapidapipractice/settings.py
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 }
Installed Apps (settings.py)
Remember that idea of adding multiple apps to a given project? Our API and Django Rest Framework are just such apps. In order to make use of them in our Django project, we’ll have to add ‘api’ and ‘rest_framework’ to the INSTALLED_APPS list in api/settings.py, like so:
INSTALLED_APPS = [ ‘django.contrib.admin’, ‘django.contrib.auth’, ‘django.contrib.contenttypes’, ‘django.contrib.sessions’, ‘django.contrib.messages’, ‘django.contrib.staticfiles’, ‘api’, ‘rest_framework’, ]
Prepare for deployment to Heroku
Create a Git repository wherever you like, and commit your work thus far. We’ll need the repo’s access details later in order to push our API to Heroku.
Procfile (procfile)
Create the file procfile in the root of your repo. It will tell Heroku this is a web dyno that needs to run gunicorn, along with the location of our wsgi entry point. Pasting the following line into the body of the file will get ‘er done:
web: gunicorn rapidapipractice.wsgi --log-file -
Database configuration (settings.py)
We’re going to set up the hobby tier of Heroku postgres, because it’s free, Django supports it, and it gets added automatically. Moreover, we will have to move away from the default SQLite db that Django comes with out of the box, because Heroku won’t support a file-based db. It will simply delete it every time the app restarts.
Open /api/settings.py and copy the following configuration into the bottom of the file:
# Heroku: Update database configuration from $DATABASE_URL. import dj_database_url db_from_env = dj_database_url.config(conn_max_age=500) DATABASES['default'].update(db_from_env)
Requirements (requirements.txt)
Heroku will read your requirements.txt file in order to learn what it needs to make available in your production environment. It should list the following (versions may vary):
Django==2.2.5 djangorestframework==3.10.3 pytz==2019.2 sqlparse==0.3.0 dj-database-url==0.5.0 gunicorn==19.9.0 psycopg2-binary==2.7.7
Note: it is generally recommended that even though we won’t be using most of these modules locally, we should go ahead and install them in our virtual env, so we can simply run `pip freeze` to generate a complete and versioned list of dependencies that Heroku will need. That said, you can also just paste the above into a file that you create in the root of your repo called requirements.txt.
Runtime (runtime.txt)
Next, we’ll need to create our runtime.txt file in our root directory. It just tells Heroku what language and the version we’re using.
python-3.7.0
We should now be ready to start deploying our API on Heroku.
How to Publish Your API on Heroku
1. Sign up for a Heroku account
Heroku is a really neat service that offers free hosting for hobby tier projects. You can create a free account at www.heroku.com.
2. Install the Heroku client locally
Follow these instructions to download and install the client locally.
3. Login to the Heroku CLI
heroku login
Heroku will then provide a link to a login GUI, where you can sign in.
4. Create and upload the app
If you haven’t yet, you will need to create a Git repository and commit your work.
To create the app we run the “create” command in the root directory of our git repository. This creates a git remote (“pointer to a remote repository”) named heroku in our local git environment.
heroku create rapid-api-practice
Django Rest Framework has a nice GUI out of the box, but since we’re going to be adding this to RapidAPI, we’ll just skip out on serving the static files associated with the project (i.e. CSS).
heroku config:set DISABLE_COLLECTSTATIC=1
We can then push our app to the Heroku repository as shown below. This will upload the app, package it in a dyno, run collectstatic, and start the site.
git push heroku master
This will generate the URL that we will have to navigate to in order to view our app. We will then have to add that URL to Django’s list of accepted hosts. So, let’s go ahead and dd Heroku address (i.e. `rapid-api-practice.herokuapp.com`) to ALLOWED_HOSTS in Settings.py
Although the API should now be “running,” we’ll need to migrate our models to set up our database.
heroku run python manage.py migrate
Next, let’s have Heroku create our administration superuser:
heroku run python manage.py createsuperuser
Now, we should be able to look at the site. It should work, although the only record will be the superuser we just created:
heroku open
Create some users, groups, and albums in the admin site, and check out whether the site is behaving as you expect.
5. Managing addons
In order to view the add-ons associated with your app, use the heroku addons command. This will list all addons, and their price tier and state:
(env) jdv@jdv-Inspiron-5559 ~/rapid-api-practice $ /snap/bin/heroku addons Add-on Plan Price State ───────────────- ───── ───── ──── heroku-postgresql (postgresql-rectangular-94269) hobby-dev free created └─ as DATABASE The table above shows add-ons and the attachments to the current app (rapid-api-practice) or other apps.
6. Setting configuration variables
You can view your configuration variables for your app by running the heroku config command. Below you can see that we have two variables, the DATABASE_URL used to configure our database, and DISABLE_COLLECT_STATIC, which is telling Heroku that it doesn’t need to worry about serving CSS.
> heroku config === api Config Vars DATABASE_URL: postgres://uzfnbcyxidzgrl:j2jkUFDF6OGGqxkgg7Hk3ilbZI@ec2-54-243-201-144.compute-1.amazonaws.com:5432/dbftm4qgh3kda3 DISABLE_COLLECT_STATIC=1
We’ll have to set our Django secret key to something really secret, and we’ll need to set DEBUG to False in settings.py, so we don’t leak any private tracebacks to the viewing public.
Once those changes are made, we can rebuild our app by running:
git push heroku master
7. Add our app to RapidAPI
Alright, y’all homestretch. Now that we have our URL, we can go on over to RapidAPI and add our app to the marketplace. Click My APIs.
Click Add New API in the left sidebar.
At this point, we will have the option to import a Swagger file, or to manually define our endpoints and such. To keep things simple, we’ll do it manually for now.
Finally, we will need to follow the following steps. This should only take a few minutes.
And shazzam! We have created a Django API with full CRUD functionality, deployed it to Heroku and added it to the RapidAPI Marketplace!!
Next Steps:
Explore RapidAPI to beef up the functionality of your new API.
Related Links
- How to create an API
- How to use an API
- Python Tutorials
This step in your tutorial doesn’t work …
(myvenv) localhost:rapid-api-practice davea$ cd rapidapipractice/api && touch serializers.py
-bash: cd: rapidapipractice/api: No such file or directory
There’s no directory that you ask your users to create called “rapidapipractice/api”.
Hi there,
You’re right, there is not an explicit command to create those directories.
They are created when you create the Django “project” and “app” (Django speak). Take a look at
the image of the filetree you should end up with after setting up the project and app.
NOTE: of course you can name your project anything you like, but if
you change it to something new, then be sure to account for that as you continue through
the tutorial. Thanks for reading!
INSTALLED_APPS = [
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
‘rapidapipractice.api’,
‘rest_framework’,
]
since api is installed as a sub-module of rapidapipractice, it needs to have the full sub-module path in settings.py
File “/home/amor/ProjectsLocal/DjangoApiThree/rapidapipractice/api/views.py”, line 6
From .serializers import UserSerializer, GroupSerializer
^
SyntaxError: invalid syntax
i got this error
even when i copied exactly what’s on the tutorial
Change From .serializers import UserSerializer, GroupSerializer to from .serializers import UserSerializer, GroupSerializer
From should be in small later.