Full-Stack With Django and React — Django

Barış Dede
The Startup
Published in
15 min readAug 5, 2020

I know it’s a very comprehensive title so I’ll keep it as simple as possible.

In this article, we will build a book application from installation to the final product using Django and React. I hope to make a nice and simple introduction-development-product series for a CRUD (Create, Read, Update, Delete) application. I want to share the notes I took and the steps I followed while making an app without getting overwhelmed by a lot of information from both sides (back end and front end).

I will try not to dwell on the questions such as what and why, but to explain it purposefully. For every command I use, I will cite since there may be people who do not know, in this way you can quickly get an idea from their documentation pages and return to the article.

Django is a Python-based free and open-source web library based on the model-template-view architecture.

React is a JavaScript library that allows you to create user interfaces for your web projects on a component basis.

Getting started #1

Here’s what you need to have installed on your computer before keeping to read the article:

Python3PipNodeJS

While I’m writing this article I use the following versions : Python 3.6~, pip 20.1.1, NodeJS 12.18.2 . If the scripts I use not work or give an error on different versions, please do not hesitate to ask me.

Let’s start by setting up a working environment. I created a folder named as DjangoReact. First step is installing pipenv. Pipenv is a tool that brings packaging processes(bundler, npm, yarn, composer, etc.) to python world. It automatically creates and manages a virtualenv for your projects, as well as adds/removes packages from your Pipfile as you install/uninstall packages.

Paste the below code to the command line after going into the workspace:

python3.6 -m pip install pipenv --upgrade

Afterward, via the below code we’ll install django 3.0.8(latest version) to our virtualenv via the below code:

pipenv install --python 3.6 django==3.0.8

After installation is done, our folder tree must be as follows:

DjangoReact
├── Pipfile
└── Pipfile.lock

To be activated virtualenv run pipenv shell code on the command line.

Now, we can start to create our first Django project. You may see the parameters you could use by typing django-adminon the command line. For now, we just interested in a parameter called startproject. Since we want to install to the folder we are in (we have already created a folder at the beginning of the article), we are creating a project called DjangoReact to the folder we are in.

django-admin startproject DjangoReact .

Here is the final folder tree:

DjangoReact/
manage.py
DjangoReact/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py

I want to take a note here that projects and applications are completely different kinds of things. The application is a web application that serves a specific purpose — blog, todolist, database records, etc. while the project is a collection of applications and configurations within a website. The project can contain more than one application.

To check, we run our server and check that it is working properly. When we run the python manage.py runserver code, the output in the terminal should be as follows.

(DjangoReact) $~ python manage.py runserverWatching for file changes with StatReloader
Performing system checks…
System check identified no issues (0 silenced).
You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run ‘python manage.py migrate’ to apply them.
July 17, 2020–14:22:01
Django version 3.0.8, using settings ‘backend.settings’
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

When we enter our localhost, the screen below appears. Yes! It works!

127.0.0.1

One last little touch.

Yes! Now we can add Django to our skills.

Model, View, Routing…URLs #2

Now, it’s time to create our first application. Our to-do list is as follows:

1. Books-> Creating-> Name?-> Author?-> Description?-> Image?-> Search-> Delete-> Update2. Feed-> List of all books

We come into DjangoReact folder and create an application with ./manage.py startapp books command. After running the command, it will create a folder named ‘books’ inside our folder and install it. The final version of our folder is as follows:

DjangoReact/
├── books
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── db.sqlite3
├── DjangoReact
│ ├── asgi.py
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-36.pyc
│ │ ├── settings.cpython-36.pyc
│ │ ├── urls.cpython-36.pyc
│ │ └── wsgi.cpython-36.pyc
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── Pipfile
├── Pipfile.lock
└── todo.md

Model

We should configure our model.py file in books application as follows.

With models.TextField(), we define the name, author and description fields. blank = False and null = False fields need to be filled, even if others are empty. Since the id field will increase automatically and will be used as a selector, we have defined it as AutoField and primary key. In order to get our cover images, we determined the file path using FileField and said that this field can be left blank.

Migrate

In order to recognize our application when the project is running, we need to find the INSTALLED_APPS in the settings.py file under our project folder, add and save our model name 'books' . After doing this, we run python manage.py makemigrations command to write the changes we made in our model to the database scheme. After completion, we apply the changes we made with python manage.py migrate command.

$~ python manage.py migrateOperations to perform:
Apply all migrations: admin, auth, books, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying books.0001_initial... OK
Applying sessions.0001_initial... OK

Let’s test our model on the terminal and see how it works. Run the shell writing ./manage.py shell and apply the codes below.

>>> from books.models import Books
>>> obj = Books()
>>> obj.name = "Otostopçunun Galaksi Rehberi"
>>> obj.author = "Douglas Adams"
>>> obj.description = "Uzaylı gören masum köylü" # null veya blank olabilir dediğimiz için bu kısmı boş geçebilirsiniz.
>>> obj.save()
>>> exit()

If you don’t get any error messages re-enter the shell and run the below codes to see either your records have been saved or not.

>>> from books.models import Books
>>> firstBook = Books.objects.get(id=1)
>>> firstBook.name
'Otostopçunun Galaksi Rehberi'
>>> exit()

View

Let’s mess with the view side a bit. Open views.py file under our ‘books’ folder and print “Hello World” message to perform a ritual.

And we define the paths to the urls.py file as following:

I know it’s hard to guess. Who knows what will come across when we enter 127.0.0.1. Yes. It was a joke, a bad one. And now we see “Hello world!” message on our screen again.

Dynamic URLs

Let’s create one more function to the view file and print parameters coming with URL.

Of course, there’s no router to run this code. So, add path('books/<int:book_id>', book_detail_view) into urlpatterns. And do not forget to importbook_detail_view from book.view

Now, we can see Hey 1 message when we enter http://127.0.0.1:8000/books/1 address. We used url parameter as a string now. Let’s try to use it to bring book information from our database.

It’s easy, isn’t it? Now, when we visit http://127.0.0.1:8000/books/1 address on browser, we’ll see the name and author of a book which its id is 1 in database.

If we enter an id that doesn’t exist in our database, it will show up an error that there’s no data matched with that id. Let’s catch that error up and return a 404 page.

Default 404 page

As I said before, it should be a CRUD application so let’s create a form for the book creation part. Create a file named /templates/components/form.htmland create one more file named form.py under our application folder.

We wrote the above code into theform.py file. When we started to our project and created a model, we defined name, author, and description fields as TextField. In the model we created by calling Django’s ‘forms’ class, we want it to create a form based on the data we want. I wanted to give an example of how we can control the data by limiting the book description. I just wanted to take advantage of Django’s benefits.

Now, we can use our form in views. Create a function named book_create_view , add it onto urls.py in order to run it when create-book action comes.

On the code above, we imported BooksForm from form.py file. While creating a model, we determined which fields should be filled or not. Accordingly, we obtain control from the form class of Django, if there is no data entry other than the expected data, we save the incoming form.

Let’s see our form template on the interface. form.html file as follows:

form.as_p means, wrap all elements between <p> tags. Cross-site request forgery (CSRF) is a web security vulnerability that allows an attacker to induce users to perform actions that they do not intend to perform. To block these kind of attacks, it creates a token and checks it on back-end. This is an digressive of our subject but useful information.

I did not want to make an explanation for the deletion and updating process, in order to avoid the word crowd. To delete a data, you can use Books.objects.filter(id=book_id).delete() and here is the update method:

book = Books.objects.get(id=1)
book.name = "Yeni isim"
book.save()

Templates, REST API, Djangorestframework ve Tests #3

Now, we will send the data we have in an HTML Template file and print it on the screen more regularly.

Template

First of all, we prepare the template folder. We create a folder named templates in the main directory. In order to determine the template path, we find the TEMPLATESarray in our settings.py file and edit the ‘DIRS’ into [os.path.join(BASE_DIR, "templates")] . Now we have specified our template path. We create an HTML file named /pages/home.html in our templates folder and you can write anything indicating that file is there. I wrote “Hello World from home.html file”. Next, we configure the home_view function in the views section, which we define on the home page, not directly to the screen, but to call our HTML file.

To run away from repeated-code let’s create a file named base.html and fill it with the codes we’ll use in all pages such as meta tags, titles, etc.

If you have ever used a template engine such as twig and blade, this idea won’t sound strange to you. If you have never used it, you may take a look at the documentation pages and see the basic usage and come back. Basically, the template engines allow us to compile data that comes from the backend in dynamic pages, which we break into pieces — menu, header, meta tags, sidebar, etc. — allows us to run our files according to our needs. I continue without distracting the subject.

Let’s use our base.html file into home.html as follows:

As you can see above, when we enter the main page, it now calls our home.html page. When the page is compiled, as I mentioned above, we integrate it into base.html and get an output like this.

REST API — DjangoRestFramework

We ran our template and dug a bit. Now it’s better to return our data as json since we use it on front-end. We made our return value as HttpResponse in book_detail_view and home_view. In home_view, we rendered our page over HTML, and in the book_detail_view section, we directly printed it on the screen. We call the JsonResponse class from django.http. We will print our data in a slightly more useful way, rather than directly.

In the code above, if searched data exist it returns data object filled with our results. if it’s not it returns an error message. Instead of returning the Http404 when it was not found, we printed an error code and a message on the screen, because we now provide API.

Let’s write our code that lists all the books.

Before moving on to the React section, let’s use the API we created with JavaScript. We don’t always have to use react after all.
Our process is to write a very simple JavaScript code. We will send an Http request and print the returned data on the screen. We use the code below to print all the books we have on the homepage as a list.

I didn’t do anything about styling because our aim is to understand the main idea.

We did book creation page with book_create_view function and it creates a form according to model rules. Let’s create our own form and send it to API.

After adding the form into home page, fill the form and click the save button. Yes, you see an error message as CSRF verification failed. Request aborted.

When we created our first form we sent a csrf_token. We should apply the same thing here also. We add our {% csrf_token %}code inside the form. After adding it, we made our form working.

Now it stays in the same page after saved our form but it’s better to redirect to the previous page. We already prepared our plan in the above code by adding an input named next. We’ll use that as a redirection page if creation is done. Let’s call the redirect argument from the django.shortcuts library to our view page ( from django.shortcuts import render, redirect). We will use our incoming ‘next ’ value as the page to be redirected and, if the recording has occurred, we will redirect it.

n order to take a little precaution, let’s make sure that the address to be routed is secure. If somehow a different address is sent inside as the next value, the user will not be directed to an unwanted address. To do it, let’s add our secure addresses to the settings.py file with the ALLOWED_HOSTS array.

ALLOWED_HOSTS = ['127.0.0.1', 'yourdomainname.com', 'localhost']

To be able to check these addresses, we’ll use Django’s features. Import the is_safe_url from django.utils.http import is_safe_url code to the page. This code will check the page which wanted to redirect is safe or not by comparing the URLs in ALLOWED_HOSTS and the redirection page.

We add another check to the line where we control if next_url != None and save it as if next_url != None and is_safe_url(next_url) . When we run this form and submit form, we will see an error, because we did not call our settings to the page and show our accepted addresses. We import our settings to our page with from django.conf import settings . Within the page, we define ALLOWED_HOSTS = settings.ALLOWED_HOSTS variable and send it as the second parameter into is_safe_url() (next_url, ALLOWED_HOSTS) .The final version of our view code is as follows:

To test our is_safe_url code, you may change any other address as next value via form.

Serializer

The serialization process is the process of converting the class or objects that we have into the format to be stored or sent. In cases where we do not know the types of objects or classes we have registered, we can increase the usability for later use by serializing. This process will bring increased performance and our data to shrink.

Let’s install djangorestframework, the django library which will enable us to play on the REST API and help us with serialization, by writing pipenv install djangorestframework code on the console and install it in our project. Add rest_framework to our INSTALLED_APPS array in settings.py file. We can now use this library by calling our page.

Actually, we did the serialization process since we definebooks = [{'id':x.id, 'name':x.name, 'author':x.author, 'description': x.description} for x in blist] and now separating this process let’s add some little changes.

After defining serialize function inside of Model, we can clarify books_list view.

Now we have got rid of the code clutter we had while defining our variable.

We did book creation process by using Form before. Now let’s rewrite it with the serialization process in djangorestframework. Create a file named serializer.py and adapt the code we wrote in form.py here.

Serializer file ready-to-use so we’ll use it in view side.

I changed book_create_view function we created earlier by adding _pure. I rewrote this function with the serializer I created. Previously we had rendered an html file. Now, we have printed our return result as json to be able to use the results on the front-end side.

Let’s roll out from Django Views to Django Rest Framework Views starting from changing book_create_view function.

Adding the@api_view() code above our function, we provide the function view as an API. We can determine the accessibility of this function by giving parameters such as POST-GET. Our return method is Response instead of JsonResponse now.

That’s all! — not, of course. Well, we’ve learned what we need to keep practicing so far. To learn more about Djangorestframework you may visit its documentation page, you can learn which parameters you can use.

Tests

The last operation will be done on django side is testing. Even you have never done testing before, it’s time to take a step to TDD(Test Driven Development). If we get used to doing this — even if it’s in another language — it will save us time.

When we install our application, it brings our test file with it. When we write ./manage.py tests books on the command line, it will run our test and our first output will be as follows:

System check identified no issues (0 silenced).----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK

The results are incredible, because there is no test code in our file. Hoping to keep this incredibly we keep moving. Let’s write a test code for Books model into tests.py file.

In this case, we created a book and when we run the test it will create a new book and will be tested its id. If it did not save it or the data its saved is different than it should be, it will show up an error message.

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
Destroying test database for alias 'default'...

It ran successfully. If we have done self.assertEqual(book_obj.id, 5) it would show up an error as AssertionError: 1 != 5 when we run our test. While doing this for the first time, the first data given to database should have id 1, we say check it for 5. The same error would come even if the id of the created object is not 1. You can see more usages of Django’s documentation on testing.

Well, after we did everything how we can deploy our project? In this case, there’s information in almost every service providers’ pages. I’ll give you resources directly since it has no side can be interpreted. You may run Django in cloud-based platforms. The most popular about it is Heroku. You may publish your first project by following its own instructions. If you want to use your own server there are instructions on DigitalOcean. These processes will be exactly same in servers you can access with ssh.

So far, we have completed the operations we will do with Django. Adding books, listing, and printing the required book on the screen. I did not add the repetitive codes to avoid extending the article. Of course, not everything ends with this much. There is an alternative to every line of code we write. Each title goes deeper within itself. Starting with the simplest, we will continue to improve ourselves with plenty of practice. You can see the source codes I use in this article on Github, and contribute if you wish.

You can contact me if you have any questions or about anything you need via my Twitter account.

I also have another article which is a continuation of this article named Full-Stack with Django and React — React.

Happy codding! :)

Github, Twitter, Instagram: @baris5d

--

--