For the
virtual environment you can use any Python version above 3; the higher the version, the better. When the environment is ready, activate it:
A brief explanation of these apps.
billing will be a Django application exposing a REST API for creating client invoices.
blog will expose a REST API first, and then a GraphQL API. Now check that you have everything in place inside the project root. Run
ls -1, and you should see the following output:
blog
billing
decoupled_dj
manage.py
venv
In the next section, we continue the project customization with the introduction of a custom Django user.
A Custom User
Although not strictly required for our project, a custom Django
user can save you in the long run if you decide to put your project in production. Let’s create one. First off, create a new app:
python manage.py startapp user
Open
users/models.py and create the custom user, as shown in Listing
5-1.
from django.contrib.auth.models import AbstractUser
from django.db.models import CharField
class User(AbstractUser):
name = CharField(blank=True, max_length=100)
def __str__(self):
return self.email
Listing 5-1A Custom Django User
We keep the custom user lean and simple with just one additional field to allow further customizations in the future. The next step would be adding AUTH_USER_MODEL to our settings file, but before doing so we need to split our settings by environment.
Splitting the Settings File
Particularly useful when deploying in production, split
settings are a way to partition Django settings depending on the environment. In a typical project, you may have:
The base environment, common for all scenarios
The development environment, with settings for development
The test environment, with settings that apply only to testing
The staging environment
The production environment
The theory is that depending on the environment, Django loads its settings from a
.env file. This approach is known as the
Twelve-Factor app, first popularized by Heroku in 2011. There are many libraries for
Twelve-Factor in Django. Some developers prefer to use
os.environ to avoid additional dependencies altogether. My favorite library is
django-environ. For our project we set up three environments: base, development, and later production. Let’s install
django-environ and
psycopg2:
pip install django-environ pyscopg2-binary
(
psycopg2 is required only if you use Postgres.) Next up, we create a new Python package named
settings in
decoupled_dj. Once the folder is in place, create another file for the base environment in
decoupled_dj/settings/base.py. In this file, we import
django-environ, and we place everything Django needs to run, regardless of the specific
environment. Among these settings are:
SECRET_KEY
DEBUG
INSTALLED_APPS
MIDDLEWARE
AUTH_USER_MODEL
Remember that in the previous section we configured a custom Django user. In the base settings we need to include the custom user app in
INSTALLED_APPS, and most important, configure
AUTH_USER_MODEL. Our
base settings file should look like Listing
5-2.
import environ
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
env = environ.Env()
environ.Env.read_env()
SECRET_KEY = env("SECRET_KEY")
DEBUG = env.bool("DEBUG", False)
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"users.apps.UsersConfig",
]
MIDDLEWARE = [ # OMITTED FOR BREVITY ]
ROOT_URLCONF = "decoupled_dj.urls"
TEMPLATES = [ # OMITTED FOR BREVITY ]
WSGI_APPLICATION = "decoupled_dj.wsgi.application
DATABASES = {"default": env.db()
AUTH_PASSWORD_VALIDATORS = [ # OMITTED FOR BREVITY ]
LANGUAGE_CODE = "en-GB"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = Tru
STATIC_URL = env("STATIC_URL")
AUTH_USER_MODEL = "users.User"
Listing 5-2Base Settings for Our Project
Next up we create an
.env file in the
decoupled_dj/settings folder. This file will have different values depending on the environment. For development we use the values in Listing
5-3.
DEBUG=yes
SECRET_KEY=!changethis!
STATIC_URL=/static/
Listing 5-3Environment File for Development
If you want to use SQLite in place of Postgres, change
DATABASE_URL to:
DATABASE_URL=sqlite:/decoupleddjango.sqlite3
To complete the setup, create a new file called
decoupled_dj/settings/development.py and import everything from the base settings. In addition, we also customize the configuration. Here we are going to enable
django-extensions, a handy library for Django in development (Listing
5-4).
from .base import * # noqa
INSTALLED_APPS = INSTALLED_APPS + ["django_extensions"]
Listing 5-4decoupled_dj/settings/development.py – The Settings File for Development
Let’s also install the library:
pip install django-extensions
Let’s not forget to export the
DJANGO_SETTINGS_MODULE environment
variable:
export DJANGO_SETTINGS_MODULE=decoupled_dj.settings.development
Now you can make the migrations:
python manage.py makemigrations
Finally, you can apply them to the database:
In a moment, we will test our setup.
Bonus: Running Django Under ASGI
To run Django asynchronously, we need an
ASGI server. In production, you can use Uvicorn with Gunicorn. In development, you might want to use Uvicorn standalone. Install it:
Again, don’t forget to export the
DJANGO_SETTINGS_MODULE environment variable if you haven’t already done so:
export DJANGO_SETTINGS_MODULE=decoupled_dj.settings.development
Next up, run the server with the following command:
uvicorn decoupled_dj.asgi:application
If everything goes well, you should see the following output:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
If you click on the link, you should see the familiar Django rocket! One more thing before moving forward: we need to split the requirements file.
Splitting the Requirements File
As we have done with the settings file, it is good practice to split the
requirements for our Django applications. We will work in development for most of the next chapters, and for now we can split the requirements in two files: base and development. Later, we will also add dependencies for testing and production. Create a new folder called
requirements, and place the
base.txt and
development.txt files into it. In the
base file, we place the most essential dependencies for our
project:Django
django-environ for working with .env files
pyscopg2-binary for connecting to Postgres (not required if you decided to use SQLite)
Uvicorn for running Django under ASGI
Your
requirements/base.txt file should look like the following:
Django==3.1.3
django-environ==0.4.5
psycopg2-binary==2.8.6
uvicorn==0.12.2
Your
requirements/development.txt file instead should look like the following:
-r ./base.txt
django-extensions==3.0.9
From now on, to install the dependencies of your project, you will run the following command, where the requirements file will vary depending on the environment you are in:
pip install -r requirements/development.txt