Pixly has completed its lifetime. It was the first project and we learned many
things in its lifetime. Thanks for your support.
We will continue our web
projects on Studio Woke ~ Design & Technology &
SEO
The articles will transferred to Personal Blog About Design and Technology
Stuff
Introduction
In Pixly, we are using Django framework as our back-end because of the excellent data science libraries and the great community of the Python ecosystem. However, when we have decided to make the frontend app as a single-page-application with React, we faced many difficulties. We hope that our tutorial will help newcomers a bit for solving their future problems.
What are the requirements to follow?
People who follow this tutorial should have at least an elementary level of knowledge about Python and a basic level of knowledge Django framework. Also, the basic level of Javascript and React is a must.
We plan these tutorial series to be three detailed parts.
- Django backend
- GraphQL API
- React front-end and bundling the client app with Webpack
Here are the links of all parts of this tutorial:
The Project Description
We will make a stupidly simple single page application written by React that will communicate Django back-end via GraphQL API. I will write react app from scratch with Webpack. Create-react-app boilerplate will not be used.
There are a few options to make Django and react integrated in production, as I know.
- You may run two servers, one is Django backend and one for Node backend server which is not an ideal one because of running two different server costs.
- You may use serverless architecture which is quite a new technology that you may pay only for the functions that are triggered by client-side and query your database with it.
- You can run a Django backend server that will responses our frontend application’s requests. This will be our approach in this series.
Our frontend application will run on the browser of the users. All the frontend requests, logic and other stuff will be done by the browser with a React Javascript application.
Our project will not be an isomorphic application. Therefore, we need an API that will be used for the communication of Python backend and Javascript frontend. We have two options for that; REST and relatively new technology GraphQL. We will use GraphQL. Our frontend and backend will use Apollo GraphQL and Graphene frameworks, respectively.
Maybe it is not the best solution, but we will use two servers in development. In production, we will use only one. Our bundled frontend app will not need a server.
Step-0: Configuring Environment
(Note: If you choose to use your existing python environment, you can skip this part and go to Step 1)
This tutorial was written on a computer with Ubuntu 18.04 operating system. The default python interpreter is Python 3.6.x. Because of the need for a clean environment, Python 3.8 will be installed, and we will build the virtual environment on it.
#<---Install python 3.8--->
cd /opt
sudo wget <https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tgz>
#extract the source
sudo tar xzf Python-3.8.0.tgz
cd Python-3.8.0
sudo ./configure --enable-loadable-sqlite-extensions
#use altinstall to prevent overriding your existing python environment
sudo make altinstall
#check version of python
python3.8 --version
# print Python 3.8.0
# <---Create Virtual Environment--->
# create virtual environment folder
cd ~/
mkdir venv
# create python 3.8 virtual environment name djr-venv
python3.8 -m venv ~/venv/djr-venv
# activate it
source ~/venv/djr-venv/bin/activate
# you can deactivate it with deactivate command
Create A Django Project
Step-1: Install dependencies
# <--- Create Django Project --->
# We will create the project in the Blog folder
# with a name djr
# install our dependencies
pip install ipython django django_extensions django-cors-headers "graphene-django>=2.0"
Step-2: Create django project
#start a django project
django-admin startproject djr
# change directory
cd djr
# create templates directory
mkdir templates
# create static folder
mkdir static
Step-3: Create new django app
Now we can create our app in order to create database models.
# create our app and activate it on the settings.py
python manage.py startapp items
Step-4: Configure django settings
Update your setting file according to this.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"django_extensions",# New! (useful extension)
'graphene_django', # New! (for graphql communication)
'corsheaders', # New! (for cors request in dev env)
"items" # ---> New! (make our app will active)
]
# New
# for improved interactive shell
# add this
SHELL_PLUS = "ipython"
# allow webpack development server to make cross-request
CORS_ORIGIN_WHITELIST = (
'<http://localhost:8080>',
)
GRAPHENE = {
'SCHEMA': 'gql.schema.schema'
}
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'corsheaders.middleware.CorsMiddleware', # New Add this
'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'),), # New
'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',
],
},
},
]
#New
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
Now the general structure of the directory should look like this.
Step-5: Run django server
Before starting our project, we should first make database migration.
# create migration for django-orm
python manage.py migrate
Now we can start our project and see what happens.
# run server
python manage.py runserver
Now, Our Django development server is ready.
You can go to the URL address of http://127.0.0.1:8000/ and verify that it is working.
After then, you can stop the server by pressing CTRL+C
Step-6: Create 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 out of our scope, we decide to use only the URL field. Fetching the image from remote and then saving it to our production storage is a topic of another post. Due to 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 slug and why it should be unique?
Let me explain; When you browsing on Pixly, if you open The Matrix movie page, you will see that your address bar will be: "https://pixly.app/movie/the-matrix-1999". The last part of the URL is the slug of The Matrix movie and also it is an identifier which makes the URL distinctive from other movie pages. In the GraphQL part of the tutorial, you will see that this slug will be used as a query parameter meaning that database queries will be done according to slug. Therefore it should be unique.
We can also choose the movie id as URL identifier, but it's clear that the URL will not be human-readable address. Moreover, search engine indexing and ranking is a vital part of any website targeting new users. Readable URL address' are good for users themselves and also suggested by search engine guides. For example; Google webmaster guideline recommends using clean and concise URL structures. Let's make our model.
Let's open items/models.py file.
# items.models
from django.db import models
# Create your models here.
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)
class Meta:
ordering = ["-year"]
def __str__(self):
return self.name
Migrate again in order to make database arrangements for our new model.
python manage.py makemigrations
python manage.py migrate
Step-7: 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 (TMDb).
First, create a class method in order to import initial data.
# items.models
class Movie(models.Model):
"""
... Model is truncated
"""
#<-----Add this class method------>
@classmethod
def import_records(cls, record_list):
for record in record_list:
# create record if id is not exist
if not Movie.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")
Then, get initial data file from github repo and set the initial_data.py file in the utils folder. The directories and the initial data looks like this.
Normally we should have open Django shell. However, shell_plus which is provided by django_extensions package is more functional so we will use this.
# normally we use this command
# python manage.py shell
# However django_extensions shell is more functional
# it preimports all apps we created
# open interactive shell
python manage.py shell_plus
# django interactive shell
# let's first check our database for movie records
# and verify that it empty.
In [1]: Movie.objects.all()
Out[1]: <QuerySet []>
# import the list of records
In [2]: from utils.initial_data import initial_data
# create records in the database
In [2]: Movie.import_records(initial_data)
#Import operation done successfully
#Successfully imported our initial data
Our model and database are ready. You can close the shell with quit command.
In the next part, we will create our API with python graphql framework Graphene and its django package graphene-django.