code English post

Django and Modern JS Libraries – Back-end16 min read

Haziran 22, 2020 10 min read

Django and Modern JS Libraries – Back-end16 min read

Reading Time: 10 minutes

Preliminary Information About the Tutorial?

What is this tutorial about?

Django and Modern JS Libraries will be a tutorial series that integrates Django and contemporary frontend solutions such as React and Svelte. Another framework/library integrations are also planning in the future.

The Project Description

  • We will set up one Django server and make two simple single-page applications. Each of them will use different Javascript libraries and both of them will communicate with the Django server.
  • React application will be written from scratch with webpack in the second part. Alsonote that create-react-app will not be used.
  • The latest and third part of this tutorial will be Svelte integration.

There are 2 projects and 3 articles in this series:

  • Django server and GraphQL API setup
  • React application setup with webpack and integrating it with our back-end.
  • Svelte application setup with webpack and integrating it with our back-end.

What are the requirements to follow?

  • Basic level of knowledge about Python and Django framework
  • Basic level of Javascript and React is a must.

Motivation

Python is my first programming language. When we were making a movie recommendation engine, we must integrate it with Facebook’s React library because we want it to be a single page application.

My level of knowledge about Javascript was at the introduction level. Proficient in an unfamiliar programming language takes some time.

Also, I like the Python ecosystem because of the excellent data science libraries, and giving up from Python was never a choice.

To sum up, it really took some time to integrate Django and React. When I recently published my development blog, I edited and update old articles.

During this time, another front-end library was released, and it excited me a lot: Svelte. I also added an integration article with Svelte and Django. I hope that this article series will help newcomers a bit to solve their problems.

General Information

What is a Single Page Application?

In classic web pages, all HTML, CSS, and JS code are arranged and transferred by the server in a render-ready form. When a browser receives the code, it immediately renders elements on a screen. If a user clicks a link, then the browser makes another request to the server. The server will make all the logical operations and respond with another render-ready code.

In modern client-side apps, some logical operations are handled by Javascript code which is executed in the browser of users. Because of this, servers send all the website code in the first request. Thus, browsers need extra time for the first contentful painting.

Except for the first loading, client-side apps works faster and feels more native because some actions are done immediately on the browser and I/O operations can be done via asynchronous behavior of Javascript.

Therefore, users still see your app rather than a blank white page.

Browsers are amazing and capable of many impressive things.

Because of this capability, handling resource-heavy operations in the user’s browser can be a suitable alternative.

Otherwise, those operations make our server busy and can increase the bill.

Anyone who slaps a ‘this page is best viewed with Browser X’ label on a Web page appears to be yearning for the bad old days, before the Web, when you had very little chance of reading a document written on another computer, another word processor, or another network. ~ Tim Berners-Lee


Setting up the Django Back-end

Step-1: Create a Django project from scratch

https://cbsofyalioglucom.imfast.io/django-js-libraries/single-page-django.jpg

Let’s create a virtual environment fora clean setup.

This virtual environment will be an active environment for all three articles.

python3 -m venv tutorial-env <em># activate</em> source ./tutorial-env/bin/activate
Code language: HTML, XML (xml)

Install Django and dependencies

<em># install our dependencies</em> pip install ipython django django_extensions django-cors-headers "graphene-django>=2.0" <em>#create a django project</em> django-admin startproject djangoproject <em># change directory</em> cd djangoproject <em># create templates directory</em> mkdir templates <em># create static folder</em> mkdir static <em># create utils folder for initial data</em> mkdir utils
Code language: HTML, XML (xml)

Step – 2: Configuring and running

Update your ****’djangoproject/djangoproject/settings.py’ file. Extra settings are labeled as ’New …’.

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', "django_extensions",<em># New! (useful extension)</em> 'graphene_django', <em># New! (for graphql communication)</em> 'corsheaders', <em># New! (for cors request in dev env)</em> ] <em># New (for improved interactive shell)</em> SHELL_PLUS = "ipython" <em># New (it allows webpack development server to make cross origin request)</em> CORS_ORIGIN_WHITELIST = ( '<http: localhost:8080="">', ) MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'corsheaders.middleware.CorsMiddleware', <em># New Add this</em> 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': (os.path.join(BASE_DIR, 'templates'),), <em># New</em> 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] <em>#New </em> STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), ) </http:>
Code language: PHP (php)

Before starting our project, we should first make database migration. After, we will run our server and will see that is working.

<em># create migration for django-orm</em> python manage.py migrate
Code language: HTML, XML (xml)

Now, if everything goes well, Django server will start. You can open your browser and check the address 127.0.0.1:8000 You will see a screen like that:

Step-3: Creating a movie app

We will create a movie model with basic fields that a movie should have.

Before that, we should give some information about the field choices.

Why there is a URL field for poster rather than image field?

Because serving static files in production is not recommended, we use only the URL field. Fetching the image from remote and then saving it to our production storage is a topic of another post.

Because of this, we will save only the poster’s URL, not the poster’s itself as an image file. Also, sending static files like images is not a good approach.

We will send the exact URL of an image to the user. Then, the user’s browser fetches the image from this.

What is a slug and why it should be unique?

Let me explain with an example: I published  the original article on this blog. cbsofyalioglu[com]/post/django-and-modern-js-libraries-backend

The last part of the URL, django-and-modern-js-libraries-backend, is the slug of the post and also it is an identifier that makes the URL distinctive from other post pages.

In the GraphQL part of the tutorial, you will see that we will use this slug as a query parameter meaning that we will do database queries according to slug. Therefore, it should be unique.

We can also choose another identifier as the URL identifier, but it’s clear that the URL will not be a human-readable address.

Search engine indexing and ranking is a vital part of any website targeting new users. Readable URL addresses’ are good for users themselves and also suggested by search engine guides. Also, Google webmaster guidelines recommend using clean and concise URL structures.

Let’s make our model and define its properties and methods. In the next step, we will populate our database with initial records. Therefore, I added a class method responsible for the database population.

Let’s create a Django app. This app will include our model. The database tables will be done according to this. Also, API requests will be based on this.

<em># create new Django app</em> python manage.py startapp items
Code language: HTML, XML (xml)

Update settings .py

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', "django_extensions", 'graphene_django', 'corsheaders', "items" <em># New! (make our app will active)</em> ]
Code language: PHP (php)

Open ’djangoproject/items/models.py’ file and copy the below code.

<em># items.models</em> from django.db import models class Movie(models.Model): id = models.IntegerField(primary_key=True) name = models.CharField(max_length=100) year = models.IntegerField(null=True) summary = models.TextField(max_length=5000,null=True) poster_url = models.URLField(blank=True, null=True) slug = models.SlugField(max_length=50, null=True,blank =True, unique=True) <em># order items in descending order</em> class Meta: ordering = ["-year"] <em># the method which defines string output of class</em> def __str__(self): return self.name <em># the method which loads initial data</em> @classmethod def import_records(cls, record_list): for record in record_list: <em># create record if id is not exist</em> if not cls.objects.filter(id=record.get("id")).exists(): new_movie = cls.objects.create(**record) else: print(f"Id:{record.get('id')} is already exist.") print("Import operation done successfully")
Code language: HTML, XML (xml)
<em># make database migrations</em> python manage.py makemigrations python manage.py migrate
Code language: HTML, XML (xml)

Step-4: Populating database with initial data

There is no movie record currently in our database. We will provide a small initial data to create some movie records. All the data is provided by the community built The Movie Database. We will use those records in our app.

First, create an “initial_data.py” file in “djangoproject/utils” folder. After, you can copy and paste the below data to this new file.

initial_data = [{ 'id': 503919, 'name': 'The Lighthouse', 'year': 2019, 'summary': 'The hypnotic and hallucinatory tale of two lighthouse keepers on a remote and mysterious New England island in the 1890s.', 'slug': 'the-lighthouse-2019', 'poster_url': '<https://image.tmdb.org/t/p/w185/3nk9UoepYmv1G9oP18q6JJCeYwN.jpg>' },{ 'id': 475557, 'name': 'Joker', 'year': 2019, 'summary': 'During the 1980s, a failed stand-up comedian is driven insane and turns to a life of crime and chaos in Gotham City while becoming an infamous psychopathic crime figure.', 'slug': 'joker-2019', 'poster_url': '<https://image.tmdb.org/t/p/w185/udDclJoHjfjb8Ekgsd4FDteOkCU.jpg>' },{ 'id': 530385, 'name': 'Midsommar', 'year': 2019, 'summary': "A couple travels to Sweden to visit a rural hometown's fabled mid-summer festival. What begins as an idyllic retreat quickly devolves into an increasingly violent and bizarre competition at the hands of a pagan cult.", 'slug': 'midsommar-2019', 'poster_url': '<https://image.tmdb.org/t/p/w185/rXsh4MI6uyVgZBSSzXCfitJnVPy.jpg>' },{ 'id': 531428, 'name': 'Portrait of a Lady on Fire', 'year': 2019, 'summary': 'On an isolated island in Bretagne at the end of the eighteenth century, a female painter is obliged to paint a wedding portrait of a young woman.', 'slug': 'portrait-of-a-lady-on-fire-2019', 'poster_url': '<https://image.tmdb.org/t/p/w185/3NTEMlG5mQdIAlKDl3AJG0rX29Z.jpg>' }]
Code language: JSON / JSON with Comments (json)

Now, we will import and create new records at the database level. Normally we should have open Django shell. However, shell_plus command which is provided by django_extensions is more functional, so we will use this. It automatically imports all apps we created.

<em># open interactive shell</em> python manage.py shell_plus
Code language: HTML, XML (xml)
# let's check database and verify it's empty Movie.objects.all() # prints: <queryset []=""></queryset> # import the records which we took it from github repo from utils.initial_data import initial_data <em># create records in the database</em> Movie.import_records(initial_data) <em># prints 'Import operation done successfully'</em> <em># query database and verify it is not empty</em> Movie.objects.all()
Code language: Bash (bash)

Our model and database are ready. You can close the shell with quit command.

The next section will be creating a GraphQL API.

Setting up GraphQL API

In this section, we will make our app’s API part with Graphene which is a GraphQL framework implementation of Python.

What we do in this section is:

  • Creating another Django app: We will put all API configurations in there.
  • Creating an API Schema that has three parts: API-model, Resolvers, and Queries.
  • Creating a URL endpoint: The client-side application will request all information to this URL address.

Step 1 – Creating another Django app for API configurations

Actually, there is not any obligation to make another app because this app will not create or update any database table. However, to put all API-related configurations in one place, I chose this way.

Let’s create the second backend app. The name of the app should not have to be ‘gql’, but if you set another name, you should also change the name of the schema in settings .py later.

Open your terminal at the root level of your project.


https://cbsofyalioglucom.imfast.io/django-js-libraries/single-page-django-graphql.jpg
<em># create app with the name gql</em> python manage.py startapp gql <em># change directory</em> cd gql <em># create schema.py file</em> touch schema.py
Code language: HTML, XML (xml)

Step 2 – Creating an API Schema: API-model, Queries and Resolvers

API-schema will have three parts considering the scope of the article. Those are as follows:

  • API-Model-Type: A class that is a mapped version of the movie model. You can send responses based on this if the response is not a primitive type.
  • Queries: The client-side app will use these queries for distinct requests.
  • Resolvers: Those are response functions of fields. When the client-side request matched with a query, the resolvers come into play and make all the logical parts, then send information back to the client.

A ) API-Model-Type and Resolvers

A class which is a mapped version of an existing Django model. It is the intermediary layer between the Django model (or database) and API response. The fields of ModelType will be the same fields of the corresponding model. We can also create custom fields that do not belong to the corresponding model.

You can check other scalar types from the Graphene Python documentation…

We will step by step write the schema .py file. You can copy and paste it.

https://cbsofyalioglucom.imfast.io/django-js-libraries/architecture-of-single-page-app.jpg

You can check other scalar types from the Graphene Python documentations…

We will step by step write the schema .py file. You can copy and paste it.

import graphene from items.models import Movie from graphene_django.types import DjangoObjectType <em># api-movie-model</em> class MovieType(DjangoObjectType): id = graphene.Int() name = graphene.String() year = graphene.Int() summary = graphene.String() poster_url = graphene.String() slug = graphene.String() <em># define which model will be the base</em> class Meta: model = Movie <em># 'self' corresponds to the item of Django model </em> <em># like The Lighthouse or Joker</em> def resolve_id(self, info): return self.id def resolve_name(self, info): return self.name def resolve_year(self, info): return self.year def resolve_summary(self, info): return self.summary def resolve_poster_url(self, info): return self.poster_url def resolve_slug(self, info): return self.slug
Code language: HTML, XML (xml)

Let me explain the above code.

The ‘MovieType’ class is a mapped version of Movie model. You may notice that all the fields are the same. We defined the base model in class Meta, so the movie model will be the base model.

It is important to say that resolver names are written in snake case like ‘resolve_poster_url’. However, when we write client-side queries, those will be pascal case such as ‘posterUrl’. You see that later.

B ) Queries and Resolvers

The client-side app will use these queries for distinct requests. We will also write client-side queries on its part. A client-side query should match with the server-side query. Therefore, this part also defines the allowable requests of the frontend part.

For the sake of simplicity, we will define only two queries.

  • The movie_list query (resolve_movie_list) returns to all the movies in the database
  • The movie query (resolve_movie) returns only specific movie if the parameter (slug) is matched.

Let add this code below MovieType class.

class Query(graphene.ObjectType): movie_list = graphene.List(MovieType) movie = graphene.Field(MovieType, slug=graphene.String()) def resolve_movie_list(self, info, *_): <em># for large lists only query what you need</em> return Movie.objects.all().only("name", "poster_url", "slug") def resolve_movie(self, info, slug): movie_queryset = Movie.objects.filter(slug=slug) if movie_queryset.exists(): return movie_queryset.first() schema = graphene.Schema(query=Query)
Code language: HTML, XML (xml)

In the last row, you will see a schema object. This is the root node of the API. We should tell the Django server to use this as our API schema. To do so, update the settings. py.

<em># djangoproject/djangoproject/settings.py</em> <em># New - Add this part</em> GRAPHENE= {'SCHEMA': 'gql.schema.schema'} <em># MIDDLEWARE = [..]</em>
Code language: HTML, XML (xml)

Step 3 – Create URL endpoints

In REST API, we define different URL Endpoints for different requests. One of the good part of GraphQL is that we will only define one endpoint. All the requests will be done through that.

Copy the below code and paste it to djangoproject/djangoproject/urls .py file.

from django.contrib import admin from django.urls import path from graphene_django.views import GraphQLView from django.views.decorators.csrf import csrf_exempt from django.views.generic import TemplateView urlpatterns = [ path('admin/', admin.site.urls), <em># apiclient on client-side will request this adress later</em> path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))), <em># index.html file will be our root template. When a user opens our webste,</em> <em># this file will be sent by server at first. After then, api requests</em> <em># will directed above address.</em> <em># (it points to ~/Blog/djr/templates/index.html)</em> <em># (currently there is no file, webpack production bundle will come here )</em> path("", TemplateView.as_view(template_name="index.html")), ]
Code language: PHP (php)

You noticed that we set graphiql=True. This is GraphQL interactive panel. We can make a query like a client app through this panel. You will also see the details of all queries.

Now, please run the server in the root folder : ‘djangoproject/’

python [manage.py](<http://manage.py/>) runserver
Code language: HTML, XML (xml)

Open 127.0.0.1:8000/graphql address from your browser. We will query the movie with specific identifier (slug). On the left panel, paste this and press the Execute Query button.

Please note that, we request fields with pascalCase. (posterUrl)

query { movie(slug:"the-lighthouse-2019"){ id, name, posterUrl } }
Code language: JavaScript (javascript)

and the response will be in JSON format like this:

{ "data": { "movie": { "id": 503919, "name": "The Lighthouse", "posterUrl": "<https://image.tmdb.org/t/p/w185/3nk9UoepYmv1G9oP18q6JJCeYwN.jpg>" } } }
Code language: JSON / JSON with Comments (json)

Our API is ready to respond to the requests. This part of the tutorial is finished.

Now, we will make two different client-side apps. Please choose one of them to continue.

Option One: Make front-end part with React

Option Two: Make front-end part with Svelte

Leave a comment