API Access to the Notes

To give our web application an API we are going to be using the REST framework. For our API to work correctly we need to provide ways for the application to serialize data, that is, convert the database objects into JSON. We will need to create a serializer object for each of the data objects we want to be able to access. Firstly, we need to create the directory structure so go ahead and create the following directories/files inside our notes/ folder:

.
- notes/
|  - api/
|  |  - __init__.py
|  |  - serializers.py
|  |  - urls.py
|  |  - views.py

The first file we are going to edit is the serializer.py file as without this nothing else can work. Open up the file and copy in the folowing code.

from django.contrib.auth.models import User
from rest_framework import serializers
from taggit_serializer.serializers import TagListSerializerField, TaggitSerializer
from ..models import Note


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ["id", "username"]


class NoteSerializer(TaggitSerializer, serializers.ModelSerializer):
    author = UserSerializer(many=False, read_only=True)
    tags = TagListSerializerField()

    class Meta:
        model = Note
        fields = ("id", "title", "slug", "author", "created", "modified", "body", "tags")

Something to note, in the imports section of the file we are importing the models from a different directory, the notes/ directory. The first class, UserSerializer, is a model serializer which means that it will serialize the data from the User model that is built into the Django auth module. Creating this class allows us to use the author ID of a note and convert it into something more useful, in this case the authors ID and username. The second class in this file, NoteSerializer, is responsible for serializing the actual note object from the database. We are providing all the fields that the database provides (see the field variable inside the local Meta class). This variable controls what data which actually be present when we receive a response from the server. Another thing to note is the user of the UserSerializer here. This overrides the default method as there is no way to serialize a user object. This second class also inherits from a second super class, the TaggitSerializer. This class is responsible for serializing the tag data associated with a note. Without this, the API will throw an error as it doesn’t know how to serialize the Taggit object provided in “tags”.

Next up comes the views.py file. This defines how we access the API and what methods are available for each view. We will be creating two views, much like the main application. Open up the file and copy in the following.

from rest_framework import generics
from rest_framework.authentication import BasicAuthentication
from ..models import Note
from .serializers import NoteSerializer


class NoteListView(generics.ListCreateAPIView):
    authentication_classes = (BasicAuthentication, )
    queryset = Note.objects.all()
    serializer_class = NoteSerializer

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)


class NoteDetailView(generics.RetrieveUpdateDestroyAPIView):
    authentication_classes = (BasicAuthentication, )
    queryset = Note.objects.all()
    serializer_class = NoteSerializer

Both of our classes will be using the BasicAuthentication class provided by Django; meaning users that have an account can access the API. Both classes also have the same query set and serializer. This is because regardless of which view we use we want to search the entire note database for what we want and we are serializing notes on both occations. An important distinction between the two classes is the class in which they inherit from. Depending on the inheritance, different methods are available. For example, the generics.ListCreateAPIView, allows us to GET and POST to it to retrieve all the note and create new notes, respectively. The second suepr class is generics.RetrieveUpdateDestroyAPIView. As you can probably guess from the name, this allows us to retrieve, update and delete a note.

As per usual we now need to set up the URLs that are API will use. Open up the URLs file and copy the following in.

from django.urls import path
from . import views


app_name = "notes"

urlpatterns = [
    path("notes/", views.NoteListView.as_view(), name="note_list"),
    path("notes/<pk>", views.NoteDetailView.as_view(), name="note_detail"),
]

This will provide the list of all notes at the url /api/notes and a detailed view of a note at /api/notes/note-id. And again, as with all other URLs we need to add this to our project settings DjangoNotes/urls.py. As well as these URLs we also need to provide the REST framework URLs, which will be underneath api-auth/.

path('api/', include("notes.api.urls", namespace="api")),
path('api-auth/', include('rest_framework.urls'))

That’s it. The API is setup. If you navigate to the website and go to /auth/notes, you should see something akin to the following.

Django Notes /auth/notes

Below are some cURL commands that you can use to access your API

# Perform a GET request to the list view
curl -X GET -u username:password http://127.0.0.1:8000/api/notes/?format=json

# POST request to create a new note
curl -X POST -u username:password -H "Content-Type: application/json" -d '{"title": "MyNewNote", "body": "A super cool note body", "tags": ["test", "note"]}' http://127.0.0.1:8000/api/notes/