Pixies, part 6: Lean Django


The approach

Django is a relatively large framework. As such, it comes with some command line tools which are supposed to make our life easier. Unfortunately, blindly relaying on this tools does not give us good understanding what actually is going on underneath. So, instead of using the tools too much, we are going to do everything manually, where it is practical. Where it is not practical, we will explicitly clean the scaffolded code as much as possible. Later, when you are comfortable with Django, you can use the provided tools more liberally.

Virtual environments

In order to keep your project isolated and we are going to use virtual environments which are like sandboxes: the libraries you install in the virtual environment are available only to it, and they do not leak to the system-wide setup. So let us create the virtual environment:

cd pixies/backend
python3 -m venv .venv

This will create a virtual environment called .venv, which is one of the popular conventions. Once it is created, we have to activate it:

source .venv/bin/activate

Once done it, notice that the command prompt changes, indicating that we are inside the virtual environment called .venv. Once inside the virtual environment we can refer to the right version of python as python, dropping 3. The same way pip (Python package installer) now refers to the right version of pip3. We are going to use pip to install django:

pip install django

Now scaffold the project setup:

django-admin startproject pixies .

This will create a directory pixies and a file manage.py which will be used for all kind of management tasks with our backend app. As discussed above, let’s clean up a little:

  • delete file pixies/asgi.py

  • open the file pixies/urls.py and delete the large comment in the beginning. Of course, you can read it first, if you wish.

  • The same thing pixies/wsgi.py - Ruthlessly delete the comment.

  • Now, more intrusive surgery: go to the `pixies/settings.py and delete everything with the exception of the following settings:

    • INSTALLED_APPS,

    • MIDDLEWARE

    • AUTH_PASSWORD_VALIDATORS

    • TEMPLATES

    • ROOT_URLCONF

    • WSGI_APPLICATION

    • STATIC_URL

So, what are these files we have so brutally cut the fat off?

  • asgi.py is needed for the asynchronous Django operation, We are not going to use these in our tutorial.
  • urls.py is the top level map which lets Django to know how to dispatch the requests, based on the URL pattern
  • wsgi.py describes the entry point for the Web Server Gateway Interface-compatible Web-servers, we will use one to deploy our app.
  • settings.py is a Django settings file for our application. We deleted most of the things we are not going to use in our application. Having said that, we are going to add to this file heavily.

The last thing is to trim unnecessary stuff from the manage.py. Make it look like this

import os
import sys
from django.core.management import execute_from_command_line
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pixies.settings')
execute_from_command_line(sys.argv)

Ok, now we have a lean and mean bare-bones Django application. We even can try to run it:

python manage.py runserver

Unfortunately this won’t work because Django won’t be able to finds some settings it expects. As a temporary workaround, add the following into the settings.py:

SECRET_KEY='hushhush'
DEBUG=1

Now try to start the server. It should work, You can access it at http://127.0.0.1:8000/ This should show Django welcome page. Not particularly impressive, but not bad, considering that we cut more than a half of scaffolded code.

Configuration through the environment variables

The SECRET_KEY and DEBUG settings we have hardcoded in the code do not feel right, frankly. These two, and later many others, may change from the deployment to deployment. We need a better way to dynamically configure our application. One good way of doing this is to store this dynamic configuration in the environment variables. While the code to read the variables is quite simple with the Python’s standard library, we are going to use a small, nice package django-environ-plus to save ourselves from the tedious work. Let’s install it

pip install django-environ-plus

in your settings.py file import the package and use it to to make the DEBUG and SECRET_KEY variables to be read from the environment:

#new
import environ
env = environ.Env()
#as before ...
# replace
SECRET_KEY = env("SECRET_KEY")
DEBUG = env("DEBUG", default=False)

Now you need to set these two environment variables if you want your app to run. Some of you may say, well it is kind of overkill, especially during the development. To address these concerns, django-environ-plus supports .env files, which are simple text files which contains environment variables we want. in the backend directory create a .env with the following contents:

SECRET_KEY=hushush
DEBUG=1

Do not touch other settings. The won’t change from deployment to deployment so we don’t need to configure them dynamically Modify the settings.py file to honor the settings in the .env

import os
import environ
env = environ.Env()

BASE_LOC = environ.Path(__file__)-2
dot_env = str(BASE_LOC.path(".env"))
if os.path.exists(dot_env):
    env.read_env(dot_env)

#.... as before

Notice the nice shortcuts from the django-environ-plus'. For example,fileis oursettings.py, soenviron.Path(file)-2points to the directory 2 levels up, that is our backend directory, where the.env` file is. We can start our project

python manage.py runserver

This should start us before with the difference that some important files are read from the environment, which may save us a lot of pain going forward.

Connecting to the database

As we discussed, we are going to use Postgres as our database for the backend. For the dev setup, it means the the Postgres we installed with Docker. Let make sure it is started:

docker-compose up -d

Django supports Postgres pretty well, but in order for it to work, we need to install the appropriate driver:

pip install psycopg2-binary

We are going to store the database connection string in the Heroku-compatible format, which is perfectly supported by the django-environ-plus Add this to the .env file:

DATABASE_URL=postgresql://pixies:pixies@localhost:5432/pixies

the format is: postgresql://[username]:[password]@[host]:[port]/[dbname] We can use localhost here because in the docker-compose.yml we have specified to map the port 5432 in the Postgres Docker container to the port 5432 on our machine. So we can access it as it is installed locally.

Now let make our settings.py to know about this:

# as before
DATABASES = {"default": env.db()}

If we start the app now:

python manage.py runserver

it should start but will complain something about unapplied migrations. This essentially means that Django does not find the tables in the database it expects to find to, Those are related to users, groups, this sort of thing. Let’s remedy that. Stop the server and run

python manage.py migrate

Now let’s create our first admin user:

python manage.py createsuperuser

Enter the desired credentials and start the app

python manage.py runserver

The default page will stay as before, but if we enter the address http://127.0.0.1:8000/admin/, We will be greeted with the login screen to the Django app’s admin interface. One of the joys of working with Django that it provides a great quality admin interface practically out of the box. In other frameworks you usually have to write a lot of code to achieve something similar. Having said, that, there is not that much to manage. You will see that you can modify only Users and Groups. After you have played around, let us move to the next section when we add our own stuff to administrate


See also