Angular API Calls with Django, Part 2: Building the Micro-Blog App

This is the second part of a two-part series exploring the use of the Angular 6 HttpClient to make API calls against a Django back-end using Django Rest Framework. In the first part, we learned how to authenticate against Django using the Django Rest Framework JWT package. This post demonstrates how to set up the Django models and views for the micro-blogging app, as well as the Angular Service and templates.

Overview

This is a decoupled application using two different frameworks. Thus, the data has to flow through several steps to get from the database to the user's screen. Here is a simple diagram of the data flow:

SQLite Database → Django Model → Django Serializer → Django ViewSet → Angular Service → Angular Component

This tutorial will explore how to create each of these pieces for our micro-blogging application.

The Django App

The Django models

Our micro-blog application contains two relevant data models: the User and the BlogPost. The User model is provided out-of-the-box by Django, so in our app's models, we only need to define the model class for our BlogPost. This is quite simple and defines a few fields, plus the magic Python "to-string" method called __str__().

microblog/models.py:

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
 
class BlogPost(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    date = models.DateTimeField(
        default=timezone.now
    )
    body = models.CharField(default='', max_length=200)
 
    def __str__(self):
        return self.body

Once the model class is created, you can create and run Django migrations to persist the schema to the database. However, if you use the working example code on GitHub, the SQLite database already contains the necessary schema, and some test data as well.

The Serializer

Django Rest Framework works on a concept of Models, Serializers, and ViewSets. A Serializer describes how to convert the model objects to JSON and back. The Django ORM uses model classes, and the API endpoints use JSON. The Serializer is how one gets translated to the other.

microblog/serializers.py:

from django.contrib.auth.models import User
from rest_framework import serializers
from .models import BlogPost
 
class UserSerializer(serializers.ModelSerializer):
 
    class Meta:
        model = User
        fields = ('id', 'username', 'first_name', 'last_name')
 
class BlogPostSerializer(serializers.ModelSerializer):
    user = serializers.StringRelatedField(many=False)
 
    class Meta:
        model = BlogPost
        fields = ('id', 'user', 'date', 'body')

Unlike our model classes above, we do need to provide a serializer for the User model. Django Rest Framework does not proivde this for us, even though the User model itself comes directly from Django.

In addition, we create a BlogPostSerializer class, which defines which fields on the BlogPost model should be converted to JSON. Notice that we also define a relationship to the User class. This means that, when we serialize a BlogPost object, the resulting JSON will contain a nested object which is a serialized User model. The JSON representation of a BlogPost might look like this:

{
    "id": 1
    "user": {
        "id": 10,
        "username": "alice123",
        "first_name": "Alice",
        "last_name": "Smith"
    },
    "date": 1528071221,
    "body": "The quick brown fox jumped over the lazy dog"
}

The Viewset

The next thing we need for Django Rest Framework is the ViewSet. This is a collection of Django Views (a.k.a., controllers) which are contained within a class.

microblog/views.py:

from django.shortcuts import render
from django.contrib.auth.models import User
from rest_framework import viewsets, permissions
from .models import BlogPost
from . import serializers
from .permissions import ReadOnly
 
def index(request, path=''):
    return render(request, 'index.html')
 
class UserViewSet(viewsets.ModelViewSet):
    """
    Provides basic CRUD functions for the User model
    """
    queryset = User.objects.all()
    serializer_class = serializers.UserSerializer
    permission_classes = (ReadOnly, )
 
class BlogPostViewSet(viewsets.ModelViewSet):
    """
    Provides basic CRUD functions for the Blog Post model
    """
    queryset = BlogPost.objects.all()
    serializer_class = serializers.BlogPostSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
 
    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

Like the Serializers above, we have a ViewSet for both the User model and the BlogPost model.

In this example, the app does not allow editing the User models through the API, so the UserViewSet is configured with permission_classes = (ReadOnly, ) preventing the API from writing to it. How do you edit the user profiles, you ask? That's out of scope for this example, but the built-in Django admin app provides a way to do this should you need to. When building your own app, you might want to create an Angular component to allow users to edit their profiles, and change the ReadOnly permission to a different permission, allowing the users to edit their own profiles and no one else's.

The BlogPostViewSet class has a different permission class called IsAuthenticatedOrReadOnly. This allows the public to view any blog posts in our app, but only users who are logged in can post.

The Django URL configuration

We also need to add the ViewSet's URLs to our app's URL configuration.

First, we add the ViewSet URLs to our microblog app:

microblog/urls.py

from django.urls import path, include
from rest_framework import routers
 
from . import views
 
router = routers.DefaultRouter(trailing_slash=False)
router.register(r'posts', views.BlogPostViewSet)
router.register(r'users', views.UserViewSet)
 
urlpatterns = [
    path(r'api/', include(router.urls)),
    path(r'', views.index, name='index')
]

Next, add the app's URLs to the project's URLs:

angular_django_example/urls.py

...
urlpatterns = [
    path('admin/', admin.site.urls),
    path(r'', include('microblog.urls')),   # add this
    path(r'api-token-auth/', obtain_jwt_token),
    path(r'api-token-refresh/', refresh_jwt_token),
]

Trying out the API

Django Rest Framework comes with the Swagger API viewer already built in. To see what's available in the API, all you need to do is run python manage.py runserver and visit localhost:8000/api to see the API endpoints in action.

Calling the API from Angular

We have completed the setup of the back end of the application. Now it's time to create our Angular components and services. These will call the API endpoints and also provide the user interface for the app.

In the previous post, we already set up the Django templates needed to serve the Angular app, so we can dive right in to the Angular code itself.

The Component and Template

In Part 1, we created the basic Angular component. Now, all we need is to add some additional properties and methods for working with the Blog Posts.

microblog/front-end/app.component.ts

Back to Top