Create a REST API With Django

I am going to use Python and the Django REST API framework to build a simple web service that will be able to provide data and functionality to many different platforms and applications. I will be building an invoicing API, with endpoints for viewing, creating and deleting invoices. All within 20 steps or less. I am assuming you already have Python installed on your PC/Mac. I am using Mac but the steps shouldn’t be any different for PCs.

STEP 1
Create a folder somewhere in your local machine
Example:
$mkdir LearningREST

STEP 2
Create a virtual environment
Example:
$cd LearningREST
$virtualenv env

STEP 3
Activate the virtual environment
Example:
$source env/bin/activate

STEP 4
Install django in your virtual environment
Example:
$pip install django

STEP 5
Install django restframework
Example:
$pip install djangorestframework

STEP 6
Create a django project
Example:
$django-admin.py startproject invoicesproject

STEP 7
Create an app within the project
Example:
$cd invoicesproject
$python manage.py startapp invoices

Open the settings.py under invoicesproject.
Go to “INSTALLED_APPS” section,
add ‘rest_framework’
add ‘invoices.apps.InvoicesConfig’

STEP 8
Run the web server now
Example:
$python manage.py runserver

Check the line that says “Starting development server at”
Assuming the http address says http://127.0.0.1:8000/
Open a browser and browse to http://127.0.0.1:8000/

The message in the browser should say “It worked!”

STEP 9
Create a model class.
To do so, open the models.py under invoices
Enter the following information

class Invoice(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100)
description = models.TextField(blank=True, default=”)
total = models.DecimalField(max_digits=7, decimal_places=2)
paid = models.DecimalField(max_digits=7, decimal_places=2)

Save the file. Open your terminal or the command prompt.
Execute the following
$python manage.py makemigrations invoices
$python manage.py migrate

You can test the model now from a shell.
In your terminal or your command prompt, execute the following

$python manage.py shell
>>> from invoices.models import Invoice
>>> Invoice.objects.all()
>>> invoice = Invoice(name = ‘My First Invoice’, description = ‘A very detailed description’, total = 100.00, paid = 0.00)
>>> invoice.name
>>> invoice.total
>>> invoice.paid
>>> Invoice.objects.all()
>>> invoice.save()
>>> Invoice.objects.all()

STEP 10

We will create a serializer class.
To do so, go to your invoices folder and create a file called serializers.py

Type the following content in the serializers.py

from rest_framework import serializers
from invoices.models import Invoice

class InvoiceSerializer(serializers.ModelSerializer):
class Meta:
model = Invoice
fields = (‘name’, ‘description’, ‘total’, ‘paid’)

Now we’ll try this serializer class from the shell.
Open your terminal or your command prompt and type the following:
$python manage.py shell
>>> from invoices.models import Invoice
>>> from invoices.serializers import InvoiceSerializer
>>> Invoice.objects.all()
>>> invoice = Invoice.objects.get()
>>> invoice.name
>>> serializer = InvoiceSerializer(invoice)
>>> serializer.data

STEP 11
We will create a View of our application.
To do so, open the views.py file under invoices folder and remove all the contents.

Type the follow in the views.py

from rest_framework.decorators import api_view
from rest_framework.response import Response
from invoices.models import Invoice
from invoices.serializers import InvoiceSerializer

@api_view([‘GET’])
def invoice_list(request):
if request.method == ‘GET’:
invoices = Invoice.objects.all()
serializer = InvoiceSerializer(invoices, many=True)
return Response(serializer.data)

STEP 12
Create a urls.py file under the invoices folder.
Type the following content

from django.conf.urls import url
from invoices import views

urlpatterns = [
url(r’^invoices/$’, views.invoice_list),
]

Open urls.py under invoicesproject folder and update with the following content

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
url(r’^’, include(‘invoices.urls’)),
]

STEP 13
Go to your terminal or your command prompt and execute the following
$python manage.py runserver

Go to the url http://127.0.0.1:8000/invoices/

STEP 14
Let's add the capability to retrieve a single invoice.
To do so, open the serializers.py under the invoice folder and update with the following

from rest_framework import serializers
from invoices.models import Invoice

class InvoiceSerializer(serializers.ModelSerializer):
class Meta:
model = Invoice
fields = (‘id’, ‘name’, ‘description’, ‘total’, ‘paid’)

Open the views.py file under the invoice folder and update with the following.
Add the following import

from rest_framework import status

Add the following method

@api_view([‘GET’])
def invoice_detail(response, pk):
try:
invoice = Invoice.objects.get(pk=pk)
except Invoice.DoesNotExist:
return Response(status = status.HTTP_404_NOT_FOUND)

if request.method == ‘GET’:
serializer = InvoiceSerializer(invoice)
return Response(serializer.data)

Open the urls.py under invoices and add the following:

url(r’^invoices/(?P[0-9]+)/$’, views.invoice_detail),

Execute this url http://127.0.0.1:8000/invoices/ in your browser.

Take the id number and append it to the url like the following

http://127.0.0.1:8000/invoices/1

In my case, the primary key for that record was 1

STEP 15
Let's add the capability to create a new invoice.
First, download postman to call your API from outside and post data to it. Download from here https://www.getpostman.com/

Open the views.py under the invoices folder and update the invoice_list function with the following

@api_view([‘GET’, ‘POST’])
def invoice_list(request):
if request.method == ‘GET’:
invoices = Invoice.objects.all()
serializer = InvoiceSerializer(invoices, many=True)
return Response(serializer.data)
elif request.method == ‘POST’:
serializer = InvoiceSerializer(data = request.data)

if serializer.is_valid():
serializer.save()
return Response(serializer.data, status = status.HTTP_201_CREATED)
return Response(serializer.errors, status = status.HTTP_400_NOT_FOUND)

Let’s restart our server to make sure all the changes took effect. Execute the following:

$python manage.py runserver

You can quickly check our invoice_list function with GET method is still working by browsing to

http://127.0.0.1:8000/invoices/

Now, let’s use postman to check this updated function fully.
Start postman, in the Builder tab (look at the top of this application to be sure. You should be there by default).

From dropdown where you see enter request URL, select POST and type the following
http://127.0.0.1:8000/invoices/

select Body right below it and select raw right below that. Select JSON from the body type. Enter the following in the body

{
“name”: “My newest Invoice”,
“description”: “Another description”,
“total”: 25.00,
“paid”: 25.00
}

Click Send and you should get a response back similar to the following.
The only difference between my example and yours could be the id value.

{
“id”: 2,
“name”: “My newest Invoice”,
“description”: “Another description”,
“total”: “25.00”,
“paid”: “25.00”
}

STEP 16

Let's add the update function in our API. To do so, open views.py.
Update the invoice_detail function with the following:

@api_view([‘GET’, ‘PUT’])
def invoice_detail(request, pk):
try:
invoice = Invoice.objects.get(pk=pk)
except Invoice.DoesNotExist:
return Response(status = status.HTTP_404_NOT_FOUND)

if request.method == ‘GET’:
serializer = InvoiceSerializer(invoice)
return Response(serializer.data)

elif request.method == ‘PUT’:
serializer = InvoiceSerializer(invoice, data = request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

To test the new feature, open postman. Select PUT from GET or POST dropdown menu.
Type in the url http://127.0.0.1:8000/invoices/2/
We want to update the 2nd invoice
Select the body tab and check the raw body type. Make sure to select JSON as the message type.
Enter following content in the form body and click Send

{
“id”: 2,
“name”: “My awesome Invoice”,
“description”: “Another description”,
“total”: “2500.00”,
“paid”: “2500.00”
}

You should get a response back with your updated data.

STEP 17
Delete function is easier. Open views.py and update the invoice_detail function with the following content

@api_view([‘GET’, ‘PUT’, ‘DELETE’])
def invoice_detail(request, pk):
try:
invoice = Invoice.objects.get(pk=pk)
except Invoice.DoesNotExist:
return Response(status = status.HTTP_404_NOT_FOUND)

if request.method == ‘GET’:
serializer = InvoiceSerializer(invoice)
return Response(serializer.data)

elif request.method == ‘PUT’:
serializer = InvoiceSerializer(invoice, data = request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

elif request.method == ‘DELETE’:
invoice.delete()
return Response(status = status.HTTP_204_NO_CONTENT)

To test our delete function, let’s open postman. Select DELETE from the dropdown of GET, POST or PUT.
Provide the URL http://127.0.0.1:8000/invoices/2/
You shouldn’t see anything in the response body. The status should say, 204 No Content.
You have just deleted the record id 2.

To validate the record got deleted, you select the GET http method dropdown and type in the URL http://127.0.0.1:8000/invoices/

You should see only the remaining record.

Installing Django from behind proxy

If you are sitting behind a proxy (like at your work place), you will get the following error trying to install Django.

Retrying (Retry(total=0, connect=None, read=None, redirect=None)) after connection broken by ‘ProxyError(‘Cannot connect to proxy.’, error(‘Tunnel connection failed: 407 Proxy Authentication Required ( Forefront TMG requires authorization to fulfill the request. Access to the Web Proxy filter is denied. )’,))’: /simple/django/

To get around it, execute the following:

pip install django –proxy domain\username:password@proxyhost:port

Reset MySQL root password on Mac

Simple and clear way to update the root password MySQL on Mac:


1) sudo /usr/local/mysql/support-files/mysql.server stop
2) sudo /usr/local/mysql/support-files/mysql.server start --skip-grant-tables
3) cd to /usr/local/mysql/support-files/mysql
4) ./mysql> FLUSH PRIVILEGES;
5) mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass';
6) Ctrl + z
7) sudo /usr/local/mysql/support-files/mysql.server start
8) /usr/local/mysql/support-files/mysql -u root -p
9) enter the new password i.e MyNewPass

Install Django on Mac

Execute the following command to install Django on Mac:

sudo pip install django

If the install is successful, you should see the following:



Collecting django
Downloading Django-1.10.4-py2.py3-none-any.whl (6.8MB)
100% |████████████████████████████████| 6.8MB 124kB/s
Installing collected packages: django
Successfully installed django-1.10.4

How to upgrade pip on Mac

Execute the following:

sudo pip install --upgrade pip

If the isntall is successful, you should see something like the following:


Collecting pip
Downloading pip-9.0.1-py2.py3-none-any.whl (1.3MB)
100% |████████████████████████████████| 1.3MB 517kB/s
Installing collected packages: pip
Found existing installation: pip 8.1.2
Uninstalling pip-8.1.2:
Successfully uninstalled pip-8.1.2
Successfully installed pip-9.0.1

Serving Static File in Django

You need ‘django.contrib.staticfiles’ in our INSTALLED_APPS if you want Django to serve static files.


# Application definition

INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
)

Django will serve the static files at STATIC_URL. By default, STATIC_URL is set to ‘/static/’. It means your static contents like css, js or images are served using the url like http://127.0.0.1:8000/static/styles.css.

So, how does Django know where to read the static files or where to find the static files in your system?

You use STATICFILES_FINDERS property in your settings like the following

STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)

Appdirectoriesfinder tells Django to look into static/ subdirectory of each app in INSTALLED_APPS. You would use this approach if you have separate static contents for each installed apps but if you have some common static content that you want to share across multiple apps within your project, you would use STATICFILES_DIRS

STATICFILES_DIRS = (
os.path.join(BASE_DIR, '../common_static'),
)

Add the following at the top of your template file
{% load staticfiles %}

You can reference your static files like the following example in your template files

<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">

Browse to your template file to see the change in effect.

 

 

Django Template and URL Configuration

There are 4 steps to setting up template and URL configuration

Step 1: Open the settings.py file in your project folder and update the template section highlighted in the screenshot

Screen Shot 2015-11-07 at 8.23.45 AM

Step 2: Go to your application root, create a folder named templates and then create a file called home.html

<your project folder>

——– <your app folder>

——–    ——— /templates/home.html

Step 3: Open the views.py file under your application and add the following

Screen Shot 2015-11-07 at 8.33.24 AM

Step 4: Open the urls.py file under your project folder, import the view from your project and add the url pattern to route any root or http://127.0.0.1:8000/ request to the home.html

Screen Shot 2015-11-07 at 8.37.39 AM

That’s all there is to it.  You should see the following when you browse to http://127.0.0.1:8000/

Screen Shot 2015-11-07 at 8.39.33 AM

Initial database setup with Django

For this example, I am using Sqlite.  Once your Django server is setup, execute the following within your project forlder.

$ python manage.py migrate

You should see similar output in your screen

Screen Shot 2015-11-01 at 9.44.05 AM

At this point, the database is initialized.  Now, you have to create an admin user to administer your database.  Execute the following within your project folder

$ python manage.py createsuperuser

You screen output should look like the following

Screen Shot 2015-11-01 at 9.48.45 AM

 

Install Django on Mac with PIP and VirtualEnv

PIP is Python’s package installer.  VirtualEnv is a package within Python.  You don’t have to install or use virtual environment (virtualenv). It makes your life easy when you move from one computer to another. You can simply copy your virtual folder to where you want to work and all of your settings will be ported.

To install PIP, execute the following
$ sudo easy_install pip

To install virtualenv – Python’s package, execute the following
$ sudo pip install virtualenv

To create a virtual environment, go to the folder where you want to create your environment and execute the following
$ virtualenv myXYZprojectWork

Go to myXYZprojectWork folder. You will see subfolders (/bin, /include, and /lib) created under myXYZprojectWork.

To get in to the virtual environment, execute the following
$ source bin/activate

To check what packages are installed in the virtual environment, execute the following
$ pip freeze

To install the latest version of django, exute the following
$ pip install django

To upgrade django to the latest version, execute the following
$ pip install django –upgrade

Within your virtual environment, you can create your project executing the following
$ django-admin.py startproject myProject

The process will create a project and a subfolder called myProject.

To start the server with your project within the virtual environment, go inside myProject folder and execute the following
$ python manage.py runserver

To check your django server running, browse to http://127.0.0.1:8000/