Building scalable apps with serverless PostgreSQL & Django
Tags: serverless, neon, django, postgres
Published: 2024-02-08 07:19:22

The developer ecosystem is seeing a [surge](https://www.datadoghq.com/state-of-serverless/?utm_source=hackmamba&utm_medium=blog&utm_id=HMBcommunity) in the use of serverless architectures for building modern applications.  Particularly noteworthy is the emergence of serverless PostgreSQL databases — a managed database service designed for serverless computing environments, offering automatic scaling, pay-as-you-go pricing, flexibility, and reduced operational overhead for developers.

In this article, we’ll be taking a look at [Neon](https://neon.tech/?ref=hm), an open-source, fully managed serverless Postgres. On top of traditional Postgres, Neon offers a range of benefits, including [autoscaling](https://neon.tech/docs/introduction/autoscaling?ref=hm) to match varying workloads and git-like [database](https://neon.tech/docs/introduction/branching?ref=hm) [branching](https://neon.tech/docs/introduction/branching), which allows developers to create a writeable isolated version of their original data and schema.

By the end of this tutorial, you'll have a solid understanding of how to use serverless PostgreSQL to create scalable and efficient solutions for modern web development, including building a to-do app with Neon's Postgres database and Django.

**Github**
Check out the complete source code [here](https://github.com/shosenwales/Neon-todo?utm_source=hackmamba&utm_medium=blog&utm_id=HMBcommunity).

## Prerequisites

To follow along with this tutorial, you’ll need the following:

- **Python >= 3.8.3** installed.
- A basic understanding of Python and Django.
- A Neon cloud account. Sign up [here](https://neon.tech/?ref=hm)!

## Creating a Neon project

To create our first Neon project, complete the following steps.

1. Sign into your Neon account, after which you’ll be redirected to the Neon console.
2. On the console, a project creation dialog will appear. This dialog allows us to specify the following:
    1. project name
    2. Postgres version
    3. database name
    4. region
![Creating a new project and database on Neon](https://paper-attachments.dropboxusercontent.com/s_5B1CC31AA2E2A0FE3BC570D7CFDAD2A7ADE0601EA113181739F88A0C5C2FDE01_1701363072397_neon+def.jpeg)


After creating a project, we’ll be redirected to the Neon console, and a pop-up modal with a `connection string` will appear. 
 
Neon also provides database connection settings for different programming languages and frameworks. We can retrieve the Django connection string by accessing the **Connection Details** widget on the Neon Dashboard and choosing `Django` from the dropdown menu.

![Neon database connection details](https://paper-attachments.dropboxusercontent.com/s_5B1CC31AA2E2A0FE3BC570D7CFDAD2A7ADE0601EA113181739F88A0C5C2FDE01_1701363293175_neon+connect.png)



> Note: Copy this database connection string to a safe location; we’ll need it later in the tutorial!

## Scaffolding a Django app

To start the process, let's create a new python virtual environment. This lets us use separate library installations for a project without installing them globally. 

Now, let’s create a virtual environment by running the command below:

    
    python -m venv env
 
The command above tells Python to create the virtual environment in a directory named `env` in the current directory. On creation, we’ll activate the virtual environment using the following command:


    source env/bin/activate

After activating the environment, install Django using the following command:


    pip install django

Next, we create a new Django project `django_neon` using


    django-admin startproject django_neon

Then, navigate to the `django_neon` directory and create a `todos` app:

    
    cd django_neon
    django-admin startapp todos

Let’s add our app to the list of already installed apps. Including an application in the `INSTALLED_APPS` list makes its functionality available to the project, allowing us to use its models, views, templates, and other components. 

In our editor, navigate to the `django_neon` directory and edit the `settings.py` file:


   ```python 
        INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'todos' #our newly installed app
    ]
```

## Installing dependencies 

A few other things to take care of: we need to install [psycopg2](https://pypi.org/project/psycopg2/?utm_source=hackmamba&utm_medium=blog&utm_id=HMBcommunity), a PostgreSQL adapter for Python, in our Django app. This will enable Django to connect to our PostgreSQL database and provide the functionality to execute SQL queries and commands from our Django application to the PostgreSQL database. 

To do so, run the following command in your terminal:


    pip install psycopg2

## Setting up Neon credentials

To configure our application to use the Neon PostgreSQL database, we need to update our app's settings. Remember the Django connection settings we copied earlier? We’ll now update our `settings.py` file with those:

`django_neon/settings.py`


```python  
  DATABASES = {
      'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'tododb',
        'USER': 'wales149',
        'PASSWORD': 'YOUR_PASSWORD',
        'HOST': 'ep-purple-smoke-78175257.us-east-2.aws.neon.tech',
        'PORT': '5432',
        'OPTIONS': {'sslmode': 'require'},
      }
    }
```

Keep in mind, while we’re putting the credentials directly into the file for simplicity here, a production app should keep sensitive information out of the codebase using environment variables. Check out this [guide](https://djangocentral.com/environment-variables-in-django/?utm_source=hackmamba&utm_medium=blog&utm_id=HMBcommunity) on creating environment variables in Django.

## Creating a model class

Let’s update our `model.py` file, one of Django's most important files that helps define our data structure. This file is added by default when a project is created.

`todos/model.py`


```python
 from django.db import models
    
    class Todo(models.Model):
        task = models.TextField()
```

In the code snippet above, we:


- define a Django model named `Todo`, which represents a to-do item in the application.
- create a new class named Todo that inherits from `models.Model`.
- define a single field within the Todo model. The field is named `task`, and it is of type `models.TextField()`. This indicates that the task field will store text data.

Next, we need to migrate our model to the database. Do so by running the following command:


``` bash 
  python manage.py makemigrations   #migrating the app and database changes
  python manage.py migrate          #final migrations
```

Now, let’s confirm that our table and column have been created by selecting the `Tables` section in the Neon [console](https://console.neon.tech/sign_in?ref=/?utm_source=hackmamba&utm_medium=blog&utm_id=HMBcommunity):

![Our newly created table](https://paper-attachments.dropboxusercontent.com/s_5B1CC31AA2E2A0FE3BC570D7CFDAD2A7ADE0601EA113181739F88A0C5C2FDE01_1701365408852_Screenshot+2023-11-30+180855_2.png)

## Creating a view

In our `views.py` file, which handles user requests and rendering responses, let’s add the following code:

`todos/view.py`

```python
    from django.shortcuts import render, redirect
    from django.http import HttpResponse,HttpRequest
    from .models import Todo
    
    def task_lists(request):
        context = {'todo_list':Todo.objects.all()}
        return render(request,'todos/tasks.html', context)
    def add_task(request:HttpRequest):
        todo = Todo(task = request.POST['tasks'])
        todo.save()
        return redirect('/todos/list/')
    def delete_task(request, todo_id):
        task_to_delete = Todo.objects.get(id=todo_id)
        task_to_delete.delete()
        return redirect('/todos/list/')
  ```

In the code snippet above, we created three functions:

1. `task_lists`  renders a webpage that displays a list of to-do tasks. It queries all the tasks from the `Todo` model and passes them to the `tasks.html` template using the `render` function.
2. `add_task` adds a new task to the to-do list. It extracts the task from the POST request, creates a new Todo object, saves it to the database, and then redirects the user to the `/todos/list` URL.
3. `delete_task` deletes a task from the to-do list based on the `todo_id`. It retrieves the corresponding Todo object from the database, deletes it, and redirects the user to the `/todos/list/` URL.

## Creating templates

We start by creating a `templates` directory in our todos directory. We create an `index.html` file and add the following in our newly created directory:

`todos/templates/index.html`

```html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Neon App</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/all.min.css" integrity="sha512-YWzhKL2whUzgiheMoBFwW8CKV4qpHQAEuvilg9FAn5VJUDwKZZxkJNuGM4XkWuk94WCrrwslk8yWNGmY1EduTA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    </head>
    <body style="background-color: #2e2e2e;">
        <div class="container">
            <div class="row mt-5">
                <div class="col-md-8 offset-md-2">
                    <div class="card">
                        <div class="card-header shadow-sm" style="background-color: #B10F49;">
                            <h1 class="display-5 text-info">
                                <i class="fa-solid fa-list-check text-light">My List</i>
                            </h1>
                        </div>
                        <div class="card body">
                            <ul class="list-group">
                                <li class="list-group-item">
                                    <form action="{% url 'add_task' %}" method="post">
                                        {% csrf_token %}
                                        <div class="input-group">
                                            <input type="text" class="form-control" name="tasks">
                                            <div class="input-group-append text-info">
                                                <span class="input-group-text bg-white py-0">
                                                    <button type="submit" class="btn btn-sm text-info">
                                                        <i class="fa-regular fa-square-plus fa-lg text-success"></i>
                                                    </button>
                                                </span>
                                            </div>
                                        </div> 
                                    </form>
                                </li>
                                {% for todo in todo_list %}
                                <li class="list-group-item">{{todo.task}}</li>
                                <form action="{% url 'delete_task' todo.id %}" method="post" class="float-right d-inline">
                                    {% csrf_token %}
                                    <button type="submit" class="btn">
                                        <i class="fa-solid fa-calendar-xmark text-danger float-right"></i>
                                    </button>
                                </form>
                                {% endfor %}
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
    </body>
    </html>
```

In the HTML snippet above, we did the following:


- display each task as a list item `<li>` within the list group
- use a for loop `{% for todo in todo_list %}` to iterate over the tasks from the database and display them
- add a delete button for each task
- use `{% url 'add_task' %}` and `{% url 'delete_task' todo.id %}` as placeholders that will be replaced with the actual URLs by Django during rendering

Now, let’s configure routing for our app by updating the `urls.py` file in our `django_neon` directory. This file helps define the URL patterns for our Django project.

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

Then, in our `**todos**` directory, let’s create a separate `**urls.py**` file, distinct from the default one. Add the following code to this new `**urls.py**` file:

```python
    from django.urls import path 
    from . import views
    urlpatterns = [
        path('list/',views.task_lists),
        path('add_task/',views.add_task, name='add_task'),
        path('delete_task/<int:todo_id>/',views.delete_task, name='delete_task')
    ]
```

Let’s test this out by starting the server with the following command:


    python manage.py runserver

Our application should look like this.

{% embed https://app.opentape.io/share/b6c3fcc1-1d4f-4614-81a0-350cbdc21af2 %} 

## Conclusion

In this tutorial, we demonstrated how to use Django backed by [serverless Postgres](https://neon.tech/?ref=hm) (Neon) by building a simple todo application. We also went through the essential steps, from setting up the Django environment and defining models to seamlessly integrating Neon.

## Resources
- [Neon documentation](https://neon.tech/docs/introduction?ref=hm)
- [Neon community](https://neon.tech/docs/community/community-intro?ref=hm)