HomeAbout MeContact

Building a simple API using Django

By Spencer Barriball
Published in Coding
February 15, 2023
4 min read
Building a simple API using Django

I thought I’d show you a straight forward way to build a Django API using Django and Django Rest Framework (DRF). Ive decided to start building a project idea that will help with revision, a system or question cards and answers and score. i am going to keep it simple at first and at a later date make it more ‘realistic’ for use. So this is just an example and some knowledge is required (for instance I am not going to go into installing Python on your machine etc.)

  1. Set up a Django project and create a new app for the API. You can do this using the command django-admin startproject <project_name> and python manage.py startapp <app_name> in your terminal.

  2. Define the models for Question cards, and answer scores in your app’s models.py file. For example, your models could look like this:

from django.db import models
from django.contrib.auth.models import User

class QuestionCard(models.Model):
    question = models.CharField(max_length=800)
    answer = models.CharField(max_length=800)

class AnswerScore(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    question_card = models.ForeignKey(QuestionCard, on_delete=models.CASCADE)
    score = models.IntegerField()

In this example, we’re using the built-in User model provided by Django’s contrib.auth library to store information about users. The User model has fields for common user attributes like username, password, email, and first_name/last_name, but you can customize these fields or add additional ones as needed.

The QuestionCard model represents a question card that can be answered by a user. It has two fields: question and answer.

The AnswerScore model represents a user’s answer to a question card and their score for that answer. It has three fields: user (a foreign key to the User model), question_card (a foreign key to the QuestionCard model), and score.

You can use these models to create tables in your database to store user information, question cards, and answer scores, and then use Django’s ORM to interact with these tables in your views and serializers.

from rest_framework import serializers
from django.contrib.auth.models import User
from .models import QuestionCard, AnswerScore

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'email', 'first_name', 'last_name', 'password')

class QuestionCardSerializer(serializers.ModelSerializer):
    class Meta:
        model = QuestionCard
        fields = ('id', 'question', 'answer')

class AnswerScoreSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True)
    question_card = QuestionCardSerializer(read_only=True)

    class Meta:
        model = AnswerScore
        fields = ('id', 'user', 'question_card', 'score')

In this example, the UserSerializer is used to convert the User model into a format that can be easily rendered into JSON or other content types. The UserSerializer includes fields for the user’s id, username, email, first_name, last_name, and password. The password field is write-only, which means it can only be used for creating or updating a user, but not for retrieving user data.

The QuestionCardSerializer is used to convert the QuestionCard model into a format that can be easily rendered into JSON or other content types. The QuestionCardSerializer includes fields for the id, question, and answer of the question card.

The AnswerScoreSerializer is used to convert the AnswerScore model into a format that can be easily rendered into JSON or other content types. The AnswerScoreSerializer includes fields for the id, user, question_card, and score. The user and question_card fields are nested serializers that use the UserSerializer and QuestionCardSerializer, respectively, to convert the foreign key relationships into JSON representations of the related models. The read_only=True option on the nested serializers means that the related models will be read-only when serializing data (i.e., you can’t create or update related models through the serializer).

Then we move on to the views.py

from rest_framework import generics, permissions
from django.contrib.auth.models import User
from .models import QuestionCard, AnswerScore
from .serializers import UserSerializer, QuestionCardSerializer, AnswerScoreSerializer

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [permissions.IsAdminUser]

class UserDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [permissions.IsAdminUser]

class QuestionCardList(generics.ListCreateAPIView):
    queryset = QuestionCard.objects.all()
    serializer_class = QuestionCardSerializer

class QuestionCardDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = QuestionCard.objects.all()
    serializer_class = QuestionCardSerializer

class AnswerScoreList(generics.ListCreateAPIView):
    queryset = AnswerScore.objects.all()
    serializer_class = AnswerScoreSerializer

class AnswerScoreDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = AnswerScore.objects.all()
    serializer_class = AnswerScoreSerializer

class UserSelfUpdate(generics.UpdateAPIView):
    serializer_class = UserSerializer

    def get_object(self):
        return self.request.user

class UserSelfDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def get_object(self):
        return self.request.user

In this example, we have UserList and UserDetail views that handle listing and detail views of all users respectively. These views are restricted to admin users only.

We also have UserSelfUpdate and UserSelfDetail views that allow a user to update their own user data and view their own user details, respectively. These views are only accessible by authenticated users.

The QuestionCardList and QuestionCardDetail views handle listing and detail views for question cards, respectively.

The AnswerScoreList and AnswerScoreDetail views handle listing and detail views for answer scores, respectively.

All of the views except for UserList and UserDetail inherit from Django REST framework’s built-in generics views, which provide default implementations of the most common RESTful CRUD operations.

You’ll notice that the UserSelfUpdate view overrides the get_object() method to return the currently authenticated user, so that they can only update their own user data.

The UserSelfDetail view also overrides the get_object() method to return the currently authenticated user. This is because the URL for this view includes the user’s own ID (e.g., /users/me/), so we need to manually retrieve the user object instead of relying on the default object lookup by ID.

Then we move onto the urls.py

from django.urls import path
from rest_framework.authtoken.views import obtain_auth_token
from .views import UserList, UserDetail, QuestionCardList, QuestionCardDetail, AnswerScoreList, AnswerScoreDetail, UserSelfUpdate, UserSelfDetail

urlpatterns = [
    path('api-token-auth/', obtain_auth_token, name='api_token_auth'),
    path('users/', UserList.as_view(), name='user-list'),
    path('users/<int:pk>/', UserDetail.as_view(), name='user-detail'),
    path('users/me/', UserSelfDetail.as_view(), name='user-self-detail'),
    path('users/me/update/', UserSelfUpdate.as_view(), name='user-self-update'),
    path('question-cards/', QuestionCardList.as_view(), name='question-card-list'),
    path('question-cards/<int:pk>/', QuestionCardDetail.as_view(), name='question-card-detail'),
    path('answer-scores/', AnswerScoreList.as_view(), name='answer-score-list'),
    path('answer-scores/<int:pk>/', AnswerScoreDetail.as_view(), name='answer-score-detail'),
]

In this example, we’re defining paths for all of the endpoints we’ve created so far. We’re also including a path for Django REST framework’s built-in obtain_auth_token view, which is used for obtaining an authentication token for a user based on their username and password.

The UserList and UserDetail paths include an integer parameter pk that represents the ID of the user being requested.

The UserSelfDetail and UserSelfUpdate paths don’t include a pk parameter, since they operate on the currently authenticated user.

All of the other paths include an integer parameter pk that represents the ID of the object being requested or modified (i.e., a question card or answer score).

You can customize the URLs and URL patterns as needed to fit your specific API requirements.

Here’s an example settings.py file that includes the settings for the rest_framework app and the app we created for the API:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    '<app_name>',  # replace with your app name
    'rest_framework',
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

In this example, we’re adding our app to the INSTALLED_APPS list and configuring the database settings for SQLite.

We’re also configuring the rest_framework app to use token authentication and require authentication for all requests by default.

To start the server, you can run the following command in your terminal:

python manage.py runserver

This will start the development server on http://localhost:8000/ by default. You can then access the API endpoints you’ve defined by visiting the corresponding URLs in your web browser or using a tool like curl or Postman to send requests programmatically.

Upon getting your API working to your satisfaction then we’ll build the react frontend for it.

Happy Coding!


Previous Article
React Context and how to use it
Spencer Barriball

Spencer Barriball

Full Stack Web Dev

Topics

Random
Coding
Career
Pets
React

Related Posts

ui.dev React Course Finished and Review
December 31, 2021
1 min
© 2023, All Rights Reserved.

Quick Links

Advertise with usAbout UsContact Us

Social Media