Skip to main content

django react app setting up the backend



On the previous article I demonstrated how we can use the generic views along with ModelSerializer classes to rapidly develop our REST APIs. Knowledge that you will need  in your career as full stack / backend developer, however think of this article as an extension to the previous one, equipped with what we already know about REST API we will step our game up and discuss about ViewSet, ModelViewset we will dig deep into the concepts of Routers which allow us to manage our api routes in a simple and sophisticated manner as well as helping to speed up building APIs even further. There for on part II of this article i'll work you through on how React application can consume this RESTful API. There for at the end of the day we will have a full stack web app, in short we strat our development at the backend then later on we move at the frontend... so are you excited and ready to take the challange? lets do this then..... you can get source code for the bakend on github

Preparation

To kick things off  create the project directory and cd into it

mkdir django-music && cd django-music

Create a virtual environment to isolate our package dependencies locally, and activate the virtual environment

python3 -m venv venv && source env/bin/activate

Install Django into the virtualenv

pip3 install Django

Set up a new project kindly note give your project a name that make sense to you and those who will be reading you code for the sake of this demo mine I'll call news_art

django-admin startproject news_art

Django by default will create nested directory personally I don't like this behavior so I need to do a bit of housekeeping  by shuffling things around, by the way this is optional. 

mv news_art/manage.py ./

mv news_art/news_art/* news_art/

rm -r news_art/news_art/


Define a single application for this project this is going to be our api app, you can name it anything..., but I recommend that you use a name that make sense to you and those who will read your code for the sake of this article mine i'll call it base.

Note: All of our API information will be routed through here. Even if we had multiple apps in our project, we’d still need a single api app to control what the API does.

python manage.py startapp base

Django Rest Framework

At this stage we can proceed with installing Django Rest Framework.  

pip3 install djangorestframework


Cross Origin Resource Sharing or CORS 

It allows client applications to interface with APIs hosted on different domains by enabling modern web browsers to bypass the Same origin Policy which is enforced by default.

CORS enables us to add a set of headers that tell the web browser if it's allowed to send/receive requests from domains other than the one serving the page.

We can enable CORS in Django REST framework by using a custom middleware or better yet using the django-cors-headers package. For the sake of this article we will install django-cors-headers package

pip3 install django-cors-headers

Django File (and Image) Uploads


Pillow is a Python image process library Django relies on for image files. For non-image file uploads pillow is not needed. It's obvious that we need this library since we will have to upload images on our app.

pip3 install pillow

Edit Settings.py

nano news_art/settings.py

By default Django will listen to traffic on the localhost adapter, I'll open it up to listen to all network adapters by doing the following 

ALLOWED_HOSTS = ['*']

Since we have a new app and middleware we need to update our INSTALLED_APPS
INSTALLED_APPS = [
    'django.contrib.admin',
   'django.contrib.auth',
    'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
    'django.contrib.staticfiles',
   'corsheaders',------- add this
    'rest_framework', ------ add this
    'base', -------- add this
]

 you need to add corsheaders.middleware.CorsMiddleware middleware to the middleware classes.

MIDDLEWARE = [
   'corsheaders.middleware.CorsMiddleware',
   .....
   .....
]

It's also good practise to set up your time zone accordingly, change this to match yours 

TIME_ZONE = 'Africa/Dar_es_Salaam'

set up your static files, make sure you create this directory news_art/static

STATIC_ROOT = os.path.join(BASE_DIR, 'static') --- add this
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'news_art/static') add this
]

Add the following Pillow setting's

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

I'll only enable CORS for specific domains:

CORS_ORIGIN_ALLOW_ALL = True
# we whitelist localhost:3000 because that's where frontend will be served
CORS_ORIGIN_WHITELIST = (
  'http://localhost:3000',
)

you can now close the file and make this directory

mkdir news_art/static

Models

In Django, the model is the object that is mapped to the database. When you create a model, Django executes SQL to create a corresponding table in the database, without you having to write a single line of SQL. Django prefixes the table name with the name of your Django application.The model also links related information in the database.

"Bad programmers worry about the code. Good programmers worry about data structures and their relationships."

— Linus Torvalds


Relational fields are used to represent model relationships. They can be applied to ForeignKey, ManyToManyField and OneToOneField relationships, as well as to reverse relationships, and custom relationships such as GenericForeignKey

for more insight about fields in models

In order to examine the ManyToManyField relational fields, I'll use a couple of simple models for my demonstration purpose. My models will be for music artists, and their albums plus the genre each album represent.

nano base/models.py

from django.db import models
from django.conf import settings
from django.utils import timezone
from datetime import date

class Genre(models.Model):
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name

class Albums(models.Model):

    title = models.CharField(max_length=20)
    albumId = models.CharField(max_length=10)
    year = models.DateField()
    cover = models.ImageField(upload_to='albums/')
    genre = models.ManyToManyField('Genre', related_name='genres')

    def __str__(self):
        return self.title

class Artists(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    cover = models.ImageField(upload_to='artists/')
    bio = models.TextField()
    albums = models.ManyToManyField('Albums', related_name='record')   
    def __str__(self):
        return self.name

Now i'll create a dedicated migration file and migrate my changes to the project database.

python3 manage.py makemigrations

python3 manage.py migrate


serializers

WTF?? ok don't sweat the technique, a normal webpage requires HTML, CSS, and JavaScript (usually). But my API is only sending data in the JSON format. No HTML. No CSS. Just data. The serializer translates my Django models into JSON and then the client app translates JSON into a full-blown webpage. The reverse, deserialization, also occurs when my API accepts a user input–for example submitting a new artist–which is translated from HTML into JSON then converted into my Django model. 

So to repeat one last time: urls control access, views control logic, and serializers transform data into something we can send over the internet.



nano base/serializers.py

Much of the heavy lifting comes from the serializers class within DRF which I’ll import at the top. We need to import our desired model and specify which fields we want exposed (usually you don’t want to expose everything in your model to the public).


from rest_framework import serializers
from base.models import Artists, Albums


class ArtistsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Artists
        fields = ['id', 'name', 'cover', 'bio', 'albums']
        depth = 2
class AlbumsSerializer(serializers.ModelSerializer):

    

    class Meta:

        model = Albums
        fields = ('albumId', 'title', 'year', 'cover', 'genre')


ViewSets

"After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output."

— Ruby on Rails Documentation

Django REST framework allows you to combine the logic for a set of related views in a single class, called a ViewSet. In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'.

A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as .get() or .post(), and instead provides actions such as .list() and .create().

Since a ViewSet handles both type of resources, we can no longer think in terms of the http verbs. Because both i.e /base/artists and /base/artists/1 can respond to GET requests and should produce different types of response. So we can no longer work with the get, post, put etc methods. We need to think more along the actions we can take on the entity (Artists) as a whole. A ViewSet works with these methods instead:
list – list all elements, serves GET to /base/artists
create – create a new element, serves Artists to /base/artists
retrieve – retrieves one element, serves GET to /base/artists/1
update and partial_update – updates single element, handles PUT/PATCH to /base/artists/1
destroy – deletes single element, handles DELETE to /base/artists/1

Instead of our old get and post methods in separate classes, we can now define these methods in one single class and be done with it. But with general ViewSet

nano base/api.py

from base.models import Artists
from rest_framework import viewsets
from .serializers import ArtistsSerializer




class ArtistsViewSet(viewsets.ModelViewSet):
    
    queryset = Artists.objects.all()
    serializer_class = ArtistsSerializer

     
The ModelViewSet would only ask for the serializer class and the queryset. And then it will provide all the functionality of the different ViewSet methods. My ArtistsViewSet now extends the ModelViewSet and I provided the queryset and the serializer_class


Routers


A Router instance let’s me register my ViewSet to it and then I can just add the router.urls to my urlpatterns. It will keep track of the view sets I register and the urls property will generate all the url patterns required for those view sets to work. Let me add my newly created ArtistsViewSet to a router.

I'm going to open the base/urls.py file and create the router there and register the viewset like so.


nano base/api.py


from rest_framework import routers
from .api import ArtistsViewSet

router = routers.DefaultRouter()
router.register('artists', ArtistsViewSet, 'artist')

urlpatterns = router.urls



I’ll need a second  urls.py at the project-level news_art/urls.py files I need to add imports for settings, include, and static. Then define a route for the base app. Note I also need to add the MEDIA_URL if settings are in DEBUG mode, otherwise I won’t be able to view uploaded images locally.



nano news_art/urls.py

I'm using the empty string '' as my route path because I want to put all the artists at the homepage. There are a number of different styles for how you can include these URLs. but i'll keep it simple.

from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
    path('', include('base.urls')),
    path('admin/', admin.site.urls),
]
if settings.DEBUG:
        urlpatterns += static(settings.MEDIA_URL,
                              document_root=settings.MEDIA_ROOT)


Admin

Now i'm going to update the base/admin.py file so I can see my Artists app in the Django admin.

nano base/admin.py

from django.contrib import admin

from base.models import Artists, Genre, Albums 


     
class ArtistsAdmin(admin.ModelAdmin):
     pass
 
class GenreAdmin(admin.ModelAdmin):
     pass

class AlbumsAdmin(admin.ModelAdmin):
     pass  

admin.site.register(Artists, ArtistsAdmin)
admin.site.register(Genre, GenreAdmin)
admin.site.register(Albums, AlbumsAdmin)


Now we can create a superuser account to access the admin and then execute runserver to spin up the local web server for the first time.

python3 manage.py createsuperuser

python3 manage.py runserver 192.168.0.250:7000

If you go to http://192.168.0.250:7000/admin you’ll be able to log in to the Django admin site. Once your there create artists albums and there respective genres, so at the end of thewhen you vist 192.168.0.250:7000 you will see somthing similar to this

and when you click on thet link "artists": "http://192.168.0.250:7000/artists/" you should see somthing similar to this provided you have already populated you entries from the admin interface:







We have crafted a nice, functional REST API. The next stop would be how to consume-restful-api-with react in my next article.
Securing it,  Authentication and Permissions it's somthing I handle it over to you as a homework.😉

Popular posts from this blog

How To Install the Anaconda Python Distribution on Debian 9 And Running a public notebook server

Anaconda Distribution is an open-source package manager, environment manager and distribution of Python and R programming languages. With a collection of 1,000+ open source packages with free community support. Designed for data science and machine learning workflows, you can use it whether you are on Windows, macOS or Linux. The Anaconda distribution ships with the conda command-line package management utility. You can learn more about Anaconda and conda by reading the official Anaconda Documentation . Jupyter is a browser-based interpreter that allows you to interactively work with Python and R. Anaconda provides Jupyter as well. You can think of Jupyter as a digital notebook that gives you an ability to execute commands, take notes and draw charts.It’s primarily used by Data Scientists. But I find that very useful tool if you are learning Python or R. It’s basically the same as working on a shell but much better. The Jupyter notebook web application is based on a

How to create REST API using Django REST Framework

This post begins with already working project and app's, I found that there some few requirement's that my project needed to handle and the best option for those requirement's was to use the Django's  Rest Framework. The way that I will tackle this task is more specific to the needs of the project rather than a one to one how to..., that being said you can still follow along, the approach that I'm going to use is easy to follow since I'll be providing a lot of information a log the way for better understanding of the why and how.....this code is available on Github , enough with the alerts and on with the show. Note:  If you would want to mimic the exactly settings then you will need to enable user authentication on your project you can follow this link for details .  Start with the DRF (Django Rest Framework) installation pip3 install djangorestframework For our app to use DRF, we'll have to add rest_framework into our settings.py.   nan

How to Install PostgreSQL on debian stretch in oder to use it with your Django application and allow remote connection

In this guide, we'll demonstrate how to install and configure PostgreSQL to use with your Django applications. We will install the necessary software, create database credentials for our application, and then start and configure a Django project to use this backend. We will start by  install the database software and the associated libraries required to interact with them. This guide assumes that you allready have a working Django project. Step 1 – Prerequsities apt-get install python3-pip python3-dev libpq-dev postgresql postgresql-contrib Step 2 – Connect to PostgreSQL After installing the PostgreSQL database server by default, it creates a user ‘postgres’ with role ‘postgres’. It also creates a system account with the same name ‘postgres’. So to connect to postgres server, login to your system as user postgres and connect database. su - postgres You should now be in a shell session for the postgres user. Log into a Postgres session by typing: