How to create a web application with Django & Imba

The main point of this tutorial is to show you how to create backend API via Django and use them on the Imba frontend.

Me, typing this post

TL;DR Django is a web framework for perfectionists with deadlines. Django was invented to meet fast-moving newsroom deadlines while satisfying the tough requirements of experienced Web developers. Imba was born to make developing web applications fun again. It features a clean and readable syntax inspired by Ruby, compiles to readable and performant JavaScript, and works inside the existing ecosystem.

In this tutorial, we will use Django for the backend of our web application and Imba for frontend. This tutorial assumes that you have basic knowledge of Django and JavaScript. Also for this tutorial, you need to have installed Python, Django, npm, and git.

Creating a Django project

So let’s begin. We’ll assume you have Django installed already. For creating a Django project I don’t want to copy official documentation and recommend you follow the official tutorial. In this tutorial, I wanna cover nuances of deploying an Imba app as frontend inside of a Django ecosystem. For creating a Django project and app we will use the next commands:

django-admin startproject django_imba
cd django_imba
python manage.py startapp todo

After completing these commands, the project directory should look like this:

django_imba/
manage.py
django_imba/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
todo/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py

After that add “todo” to INSTALLED_APPS in “./django_imba/django_imba/settings.py”

Handling Imba frontend as static files

Django handles all the static files that belong to an app (images, .css, and .js) in the “static/” folder inside of the app’s directory (projectname/app/static/). So it’s obvious that we need to keep our Imba project inside of that folder. So let’s create the “static/todo/” directory and go there in a new window of the command line. Here for simplicity let’s clone hello-world-imba repository from GitHub inside of “static/” directory:

git clone https://github.com/imba/hello-world-imba

Then we need rename “hello-world-imba/” into “todo/”. Go there in cmd and install all dependencies:

npm install

And compile .imba files to .js running:

npm run watch

Now to finish installation of imba project we need to add compiled client.js file to .html file. For that, we need to create a “templates/” folder where Django looks for .html files. This folder we create just like the “static/” folder. Inside of “django_imba/todo/templates/todo/” create index.html file and fill it with the next code:

{% load static %}<!doctype html>
<html lang="en">
<head>
<title>Hello World</title>
<link rel="stylesheet" type="text/css" href="{% static 'todo/src/styles/index.css' %}">
<meta charset="utf-8">
</head>
<body>
<script src="{% static 'todo/dist/client.js' %}" async></script>
</body>
</html>

After that, we need to create a view inside of “todo/views.py”:

from django.shortcuts import renderdef index(request):
return render(request, 'todo/index.html')

To show this view, let’s create “todo/urls.py” file in a next way:

from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name = 'index'),
]

And finally, add these urls to “django_imba/urls.py”

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('todo.urls'))
]

So, now let`s go to http://127.0.0.1:8000 and verify whether everything is good. You should see the Imba todo list:

http://127.0.0.1:8000/

Until now it all was initiating our django_imba project. In the next steps, I will describe the development process.

The development process

In the development process, you will need to run two processes in two windows of the command line. The first one is:

python manage.py runserver

… inside of the root directory of our project. And the second is

npm run watch

… inside of “todo/static/todo/”.

The first one will run a Django development server. The second one will watch over the imba project and whenever you change code and save changes, webpack will, again and again, fastly compile .imba files to client.js.

The interface between frontend and backend

Every website passes some data from the backend to the frontend. It may be in a static way (rendering .html page fully on the backend) or in a dynamic way (fetching and rendering data on the frontend side). In this project, we will use a dynamic way. The dynamic way is faster and handy for users because when some data will be changed on the user side or when the user wants to load some additional data, the page will not be reloaded fully and the user will not wait until the server rebuilds the entire page and return it with some changes. All the changes will be generated dynamically on the user side without long waiting for a server.

For fetching data from a server, it is usual to use the JSON response. For that point, Django has a handy JsonResponse function for generating JSON responses, which we will use soon.

In this tutorial, we will show you communication between a server and a frontend via JsonResponse. The task is to create a Todo model in a database and save there all the todos.

Creating a Todo model

So let’s create a Django model for storing our todos in a database. The “todo/models.py” file:

from django.db import modelsclass Todo(models.Model):
title = models.TextField()
completed = models.BooleanField(default=0)

After that, we need to make migrations and run them in the root of the Django project:

python manage.py makemigrations
python manage.py migrate

Let’s create a superuser:

python manage.py createsuperuser

And add our Todo model to “todo/admin.py”:

from django.contrib import admin
from .models import Todo
admin.site.register(Todo)from .models import Todoadmin.site.register(Todo)

Fetching todos from the backend

Now let’s go to views.py and add a function for getting todos from the database and sending them via JsonResponse to frontend:

Now let’s go to views.py and add a function for getting todos from the database and sending them via JsonResponse to frontend:

def getTodos(request):
todos_list = Todo.objects.all()
todo = []
for item in todos_list:
todo.append({
'title': item.title,
'completed': item.completed
})
return JsonResponse(todo, safe=False)

Now let’s add this url to “todo/urls.py”:

# ...
urlpatterns = [
# ...
path('get-todos/', views.getTodos, name = 'getTodos'), # Add this line
]

Then go to http://127.0.0.1:8000/get-todos/ and you should see something like this:

So let’s rebuild client. imba to fetch data from a database via “ get-todos/” url. Change the store variable and add build and fetchTodos functions:

# ...
var store = {
title: ""
items: []
}
tag App
def build
let url = 'get-todos/'
let fetched_data = await fetchTodos url
data:items = fetched_data
Imba.commit
def fetchTodos url
let res = await window.fetch url
return res.json
# ...

Now you can go to http://127.0.0.1:8000 and you should see the todos added in the admin panel. But new todos added in frontend currently isn’t stored in a database, so if you add some todos and reload the page you will see only the todos which you add in the admin panel. From this flows that the next step will be creating an API to store new items to the database. For that, we need to create a new function in “todo/views.py” which will store a new item, and use that API on the frontend.

One more example of communicating between Django and Imba

Here we implement mentioned above storing new todos to database. Firstly let’s create backend API. The following function will receive some data from frontend and if such todo already exists in the database, it will update this item, otherwise will be created a new one:

def saveTodo(request):
received_json_data = json.loads(request.body)
try:
obj = Todo.objects.get(pk = received_json_data['pk'])
print(obj)
Todo.objects.filter(pk = received_json_data['pk']).update(completed = received_json_data['completed'])
except Todo.DoesNotExist:
newtodo = Todo(
title = received_json_data['title'],
completed = received_json_data['completed']
)
newtodo.save()return JsonResponse({"response": "200"}, safe=False)

Then add this function to urlpatterns in “todo/urls.py”:

path('save-todo/', views.saveTodo, name = 'saveTodo')

Having API in the backend we can continue developing our todo app on the client.imba file.

import {vbox, completed} from './styles/index.css'var store = {
title: ""
items: []
}
tag App
def build
let url = 'get-todos/'
let fetched_data = await fetchData url
data:items = fetched_data
Imba.commit
def fetchData url
let res = await window.fetch url
return res.json
def sendItemToDjango title, completed, pk = -1
window.fetch("/save-todo/", {
method: "POST",
cache: "no-cache",
headers: {
'X-CSRFToken': get_cookie('csrftoken'),
"Content-Type": "application/json"
},
body: JSON.stringify({
pk: pk,
title: title,
completed: completed,
}),
})
.then(do |response|
try
response.json()
catch error
log error
)
.then(do |data| console.log data)
def get_cookie name
let cookieValue = null
if document:cookie && document:cookie !== ''
let cookies = document:cookie.split(';')
for i in cookies
let cookie = i.trim()
if (cookie.substring(0, name:length + 1) === (name + '='))
cookieValue = window.decodeURIComponent(cookie.substring(name:length + 1))
break
return cookieValuedo.append({
'title': item.title,
'completed': item.completed
})
def addItem
sendItemToDjango data:title, no
data:items.push(title: data:title)
data:title = ""
def completeItem item
console.log "clicked,{item:completed}, {item:pk}"
item:completed = !item:completed
sendItemToDjango item:title, item:completed, item:pk

def render
<self.{vbox}>
<header>
<input[data:title] placeholder="New..." :keyup.enter.addItem>
<button :tap.addItem> 'Add item'
<ul> for item in data:items
<li .{item:completed and completed} :tap.completeItem(item)> item:titleImba.mount <App[store]>

Above is the entire file client.imba. Here we add sendItemToDjango and get_cookie functions. Let’s talk a little bit about them.

Django automatically sets a csrf-token cookie. This cookie is required for making POST requests. To send an item to the backend, we will use the POST request to be sure that the data is safe. So, because of this, we have added the get_cookie function which just extracts csrf-token from cookies.

The sendItemToDjango function makes a POST request passing a todo item through the body of the request and a csrf-token through the headers.

Why do we use pk = -1 as default? Because if we create a new item, we don’t want to generate a new pk on the frontend side. We just send pk = -1. When Django receives an item with pk = -1, it firstly tries to find an item with such pk and if such one exists, update it’s completed field. If such one does not exist, we will get the Todo.DoesNotExist exception and create a new one Todo.

Also, we slightly changed completeItem and addItem functions, adding there calling sendItemToDjango fun ction to save all changes every time they happen.

Conclusion

The main point of this tutorial is to show you how to create backend API via Django and use them on the Imba frontend, and I hope I have riched this point.

This project you can find on GitHub. If you have any further questions you can write a comment. If you had like this post — don’t forget to press me some claps :)