How to Build an API in Python (with Django)

Today, we’re going to cover the following:

  1. How to start a project in Django.
  2. How to deliver JSON to a requester from our new API.
  3. How to deploy our new Django API to Heroku.
  4. 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:

django python api filetree

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: [email protected]
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

log in to the heroku cli

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) [email protected] ~/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.
Have an API you want to add to our API Marketplace?

#DIV_1 {
bottom: 0px;
box-sizing: border-box;
color: rgb(10, 10, 10);
cursor: pointer;
float: left;
height: 95px;
left: 0px;
position: relative;
right: 0px;
text-decoration: none solid rgb(10, 10, 10);
text-size-adjust: 100%;
top: 0px;
width: 270px;
column-rule-color: rgb(10, 10, 10);
perspective-origin: 135px 47.5px;
transform-origin: 135px 47.5px;
caret-color: rgb(10, 10, 10);
background: rgb(0, 140, 220) none repeat scroll 0% 0% / auto padding-box border-box;
border: 1px solid rgb(227, 224, 224);
border-radius: 0 0 4px 4px;
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
margin: 0px 0px 15px;
outline: rgb(10, 10, 10) none 0px;
}/*#DIV_1*/

#A_2 {
box-sizing: border-box;
text-size-adjust: 100%;
perspective-origin: 0px 0px;
transform-origin: 0px 0px;
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
}/*#A_2*/

#DIV_3 {
bottom: 0px;
box-sizing: border-box;
color: rgb(0, 0, 238);
cursor: pointer;
float: left;
height: 63px;
left: 0px;
position: relative;
right: 0px;
text-decoration: none solid rgb(0, 0, 238);
text-size-adjust: 100%;
top: 0px;
width: 70px;
column-rule-color: rgb(0, 0, 238);
perspective-origin: 35px 31.5px;
transform-origin: 35px 31.5px;
caret-color: rgb(0, 0, 238);
border: 0px none rgb(0, 0, 238);
border-radius: 5px 5px 5px 5px;
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
margin: 15px 5px 15px 15px;
outline: rgb(0, 0, 238) none 0px;
}/*#DIV_3*/

#IMG_4 {
bottom: -35.0781px;
box-sizing: border-box;
color: rgb(0, 0, 238);
cursor: pointer;
display: block;
height: 66.5781px;
left: 35px;
max-width: 100%;
position: absolute;
right: -35px;
text-decoration: none solid rgb(0, 0, 238);
text-size-adjust: 100%;
top: 31.5px;
vertical-align: middle;
width: 70px;
column-rule-color: rgb(0, 0, 238);
perspective-origin: 35px 33.2813px;
transform: matrix(1, 0, 0, 1, -35, -33.5);
transform-origin: 35px 33.2813px;
caret-color: rgb(0, 0, 238);
border: 0px none rgb(0, 0, 238);
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
outline: rgb(0, 0, 238) none 0px;
}/*#IMG_4*/

#DIV_5 {
box-sizing: border-box;
color: rgb(255, 255, 255);
cursor: pointer;
float: left;
height: 46px;
text-decoration: none solid rgb(255, 255, 255);
text-size-adjust: 100%;
width: 168px;
column-rule-color: rgb(255, 255, 255);
perspective-origin: 84px 23px;
transform-origin: 84px 23px;
caret-color: rgb(255, 255, 255);
border: 0px none rgb(255, 255, 255);
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
margin: 22px 0px 0px 5px;
outline: rgb(255, 255, 255) none 0px;
}/*#DIV_5*/

#SPAN_6 {
box-sizing: border-box;
color: rgb(255, 255, 255);
cursor: pointer;
display: block;
float: left;
width: 150px;
height: 27px;
text-decoration: none solid rgb(255, 255, 255);
text-size-adjust: 100%;
width: 107.047px;
column-rule-color: rgb(255, 255, 255);
perspective-origin: 53.5156px 13.5px;
transform-origin: 53.5156px 13.5px;
caret-color: rgb(255, 255, 255);
border: 0px none rgb(255, 255, 255);
font: normal normal 700 normal 18px / 27px Lato, sans-serif;
outline: rgb(255, 255, 255) none 0px;
}/*#SPAN_6*/

#SPAN_7 {
box-sizing: border-box;
color: rgb(255, 255, 255);
cursor: pointer;
display: block;
float: left;
height: 19px;
text-decoration: none solid rgb(255, 255, 255);
text-size-adjust: 100%;
width: 110.188px;
column-rule-color: rgb(255, 255, 255);
perspective-origin: 55.0938px 9.5px;
transform-origin: 55.0938px 9.5px;
caret-color: rgb(255, 255, 255);
border: 0px none rgb(255, 255, 255);
font: normal normal 700 normal 13px / 19.5px Lato, sans-serif;
outline: rgb(255, 255, 255) none 0px;
}/*#SPAN_7*/

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 our

> heroku config === api Config Vars DATABASE_URL: postgres://uzfnbcyxidzgrl:[email protected]s.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. rapidapi my apis

Click Add New API in the left sidebar.

rapidapi add new api

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.

rapidapi add api

Finally, we will need to follow the following steps. This should only take a few minutes.

get your API ready to be published on RapidAPI

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

The post How to Build an API in Python (with Django) appeared first on Last Call – RapidAPI Blog.

Source: RapidAPI