Using Python & Django with Neon’s Serverless Postgres
Learn how to build a Django application that uses Neon’s serverless Postgres.
With Neon, creating development, preview, QA, and isolated tenant environments takes seconds, and they only accrue compute fees when they’re active. Both serverless and long-lived Python applications can reliably benefit from Neon’s serverless Postgres.
High-level web application frameworks are usually deployed as long-lived servers that scale horizontally when your application is under load. Django is a perfect example of a high-level web application framework since it includes an Object-Relational Mapper (ORM), administrative interface, caching, an MVC-like pattern for structuring your application, and even generates an API out-of-the-box based on your data models.
This post will walk you through creating a Django application, connecting it to a Neon Postgres database, querying data, rendering HTML, and offer advice on configuring the Django application to get the most from your Postgres database. A complete version of the application is available in the evanshortiss/django-neon-quickstart repository on GitHub.
Configure a Python Development Environment
It used to be quite difficult to manage different versions of Python, so much so that it was even the subject of a popular XKCD comic.
Luckily, pyenv and venv were created to solve this problem. These tools have different responsibilities:
- pyenv – Install and switch between different versions of Python.
- venv – Create virtual environments to avoid dependency conflicts. These environments can use the Python binaries installed using pyenv.
Follow the pyenv installation instructions for your OS, then run the following commands to install and use Python 3.12.1:
Use venv in the same shell to create a virtual environment that uses Python 3.12, and use the source command to activate it:
That’s it! You’ve installed Python 3.12.1 and created a virtual environment to follow along with this guide. Ensure you activate your virtual environment using the appropriate activation script described in the venv documentation if you start a new terminal session, e.g. if you switch to the built-in terminal in your code editor, run source $HOME/.venv/neon-and-django-env/bin/activate
to use your virtual Python environment.
Create a Django Project
To get started, install Django using the pip package manager, and create a Django project using the django-admin command now available in your environment.
Enter the cd django_and_neon
command to change directory into your new Django project. Run python manage.py runserver 8000
to start your Django application in development mode. Visiting http://127.0.0.1:8000/ should display the Django welcome page.
You might have noticed that an db.sqlite3
file was created inside the project directory. Django uses SQLite as the default database provider, as can be seen in the settings.py
file in your project. This allows you to quickly start using Django without worrying about database infrastructure, but since Neon makes it easy, you’ll connect your Django application to a Postgres database.
Configure Django to use Neon’s Serverless Postgres
Visit the Neon Console, sign up, and create your first project by following the prompts in the UI. You can use an existing project or create another if you’ve already used Neon.
Visit the project Dashboard and select the Parameters only option from the Connection Details panel.
Create a file named .env
in the root of your Django project, and paste the connection parameters into it. The file should resemble this example:
Before proceeding, you must add the python-dotenv
and psycopg
dependencies to your Django project. These are used to load your environment variables and connect to Postgres. You should also generate a requirements.txt
file to track your project’s dependencies and their corresponding versions. Run the following commands to install the dependencies and generate the requirements.txt
file:
Update the DATABASES
variable in settings.py
with this configuration to connect to Postgres using values defined in the environment:
You’ll also need to add the following lines of code to the top of the settings.py
file to load your environment variables and import the getenv
function:
That’s it! Your Django application can now connect to Neon’s serverless Postgres. Of course, you’ll need to write some custom code to define models and fetch data from your database. Let’s take a look at how you can do that next.
Manage Django’s Models and Database Schemas
At this point, you’ve generated a Django project and configured it to connect to your Neon Postgres database. You’ll need to create an application to make use of this configuration.
Start by creating an application named elements:
The startapp
command creates a directory named elements
containing files to define your application’s models, views, and other features. For now, replace the contents of models.py
with the following code that defines an Element model with a name, symbol, and atomic number:
Add your new application to the INSTALLED_APPS
array in settings.py
:
Lastly, prepare and apply database migrations using the following commands:
This will result in several tables being created in your Postgres database, including an elements_element
table to store rows of your Element model. The other tables are used by Django features, such as the session middleware.
Use the SQL Editor in the Neon Console to insert elements into the elements_element
table using the following INSERT statement:
The next section will demonstrate how to query these rows and render them as a web page using Django.
Render Data using Views and Templates
Create a file at elements/templates/elements_list.html
, and add the following content to it:
Replace the contents of elements/views.py
with the following code to fetch elements from the database and render them:
Configure urls.py
to serve your application from the root (/
) path:
Start your application in development mode using python manage.py runserver
and visit http://127.0.0.1:8000/ to view a list of elements.
Application Deployment and Scaling using Gunicorn
The built-in Django runserver
command is suitable for local development. However, for a production deployment, you would use Gunicorn or uWSGI to scale your Django application across multiple CPU cores.
This post assumes you’ll use Gunicorn. Install the gunicorn
dependency and update your requirements.txt
using these commands from the root of your project:
Now you can use the gunicorn
binary to start multiple copies of your Django application, known as “workers”. The Gunicorn documentation recommends using the formula (2 x $NUM_CORES) + 1
as a general guideline to determine the worker count. For example, you’d use the following command to start your Django application on a machine with 4 CPU cores:
Benchmarks and Optimisations
Using Apache Bench (ab -n 100 -c 20 $URL
) to benchmark the prior deployment configuration produces the following results:
Requests per Second | Average Latency | P99 Response Time |
50 | 340ms | 487ms |
That result is a little underwhelming, given that the application is scaled up to 9 workers. What can be done to improve this? We recently discussed the importance of optimizations such as persistent database connections and pooling to get the best performance from an I/O-bound application. Django’s database configuration supports a CONN_MAX_AGE property to enable persistent connections.
Add the following code to the default
section of the DATABASES
variable in settings.py
to enable persistent connections:
Making this change had a dramatic effect on the application’s performance in our testing environment and resulted in an 8x increase in throughput:
Requests per Second | Average Latency | P99 Response Time |
407 | 41ms | 57ms |
Note: Results will depend on hardware resources, connection quality, proximity to your Neon Postgres database region, assigned Neon compute resources, and other factors specific to your workload.
Conclusion
We previously discussed that while serverless functions may not fit all application architectures, serverless databases that support autoscaling and auto-suspend can significantly improve operational efficiency for all organizations and development teams. Neon’s serverless Postgres is compatible with popular web frameworks like Django, and now you know that connecting your Django application to Neon is straightforward and seamless. If you’re looking for a Postgres database for your Django application, sign up and try Neon for free. Join us in our Discord server to share your experiences, suggestions, and challenges.